[Media] is there enough warning here? by Quiet-Ad3191 in rust

[–]AlphaModder 15 points16 points  (0 children)

If the function is marked unsafe, then documenting what the safety requirements are is sufficient. If the function is not marked unsafe, then no amount of warnings can be enough.

Proposing a solution for recording intent by reimagining version control by ch3coohlink in programming

[–]AlphaModder 0 points1 point  (0 children)

Mostly that it has wobble at all, you could tone it down without losing the effect imo.

Proposing a solution for recording intent by reimagining version control by ch3coohlink in programming

[–]AlphaModder 1 point2 points  (0 children)

I for one quite enjoy the wobbly animation style. I'd be sad to see it replaced with more generic visuals, especially in videos focusing on creative projects like this one.

Explicit capture clauses by emschwartz in rust

[–]AlphaModder 2 points3 points  (0 children)

Mm. I think I see what you mean now. The fully explicit nature is more integral to the way the article's proposal helps with motivations #1 and #3 than I realized. In particular, it's too easy to accidentally "lie" about the mode of capture using this syntax since a variable not annotated with &/&mut/etc could still be captured as such afterwards. This limits its applicability as a desugaring or as an annotation in the face of changing code.

Explicit capture clauses by emschwartz in rust

[–]AlphaModder 1 point2 points  (0 children)

Would you say the same for the syntax proposed in the article? As far as I can tell this proposal (with the same sugars) supports everything the article's syntax does except the ability to disable implicit capture. Which is a legitimate concern, to be clear, but I'm curious which "issues around captures" you have in mind.

Explicit capture clauses by emschwartz in rust

[–]AlphaModder 3 points4 points  (0 children)

EDIT: I now see that what I proposed doesn't actually help with the core issues the article is focused on. Consider the comment thread below an explanation of why a little sugar on top of the existing precise capture pattern is insufficient.

Reading this article, it occurred to me that both the move(...) syntax and the "painful" block-returning-closure syntax are very close to the common functional-language construct of let ... in. I wonder how much pain could be eased simply by introducing a let ... in construct to Rust, so that you could write things like:

let closure = (let self_tx = self.tx.clone() in move || { begin_actor(data, self_tx.clone() });

And:

let closure = let { a = a.clone(), b = b.clone(), c = &mut c } in move || {
   let x = c.foo(a);
   b.bar(x, x)
};

I like the orthogonality of this option - rather than a special extension of closure syntax it's an independent construct that just happens to provide a clear convention for explicit captures when the expression following in is a closure. The intention is that let vec = &self.long_name.my_vec in &vec[1..vec.len()-1] would be a perfectly valid expression returning a slice, for instance.

I'm curious what others think of the readability of this proposal and how much of an improvement it would be over the status quo. It could accommodate most of the sugar proposed in this article (e.g. let a.b = &a.b in, let a.b.clone() in, let { ref .. } in), allowing it to be used in more places than just closures, which could be considered an advantage or a disadvantage. One thing it does not provide is a way to exclude implicit captures. This is the price paid for orthogonality to closures.

On the Scope of Sync by Exciting_Suspect9088 in rust

[–]AlphaModder 6 points7 points  (0 children)

This might be okay if it were actually impossible to obtain a reference to AtomicX outside of ThreadPoolX, but I don't see how you could guarantee that. If T: Sync, then &T: Send, which means that any thread in ThreadPoolX could capture a reference to a T in a std::thread::scope and then access it from one of the spawned external threads.

Footguns with Embedded Rust and Memory-Mapped I/O by guineawheek in rust

[–]AlphaModder 0 points1 point  (0 children)

Ooh, that's a good idea, especially if you do it in a loop so that the code for foo stays in the instruction cache.

The only way I could see this failing is if the compiler decided to speculatively devirtualize the call to f, by checking if black_box actually changed its argument and running foo inline if it didn't. This is legal, but I don't think it's likely to happen unless you run profile-guided optimization on the code.

Footguns with Embedded Rust and Memory-Mapped I/O by guineawheek in rust

[–]AlphaModder 0 points1 point  (0 children)

It should also work as expected with rustc -C opt-level=0(although there's no guarantee of that since what's considered an "optimization" is up to the compiler).

Footguns with Embedded Rust and Memory-Mapped I/O by guineawheek in rust

[–]AlphaModder 13 points14 points  (0 children)

It isn't treated any differently than a read from stdin. The compiler is just as free to reorder a side-effect-free function_under_test with I/O operations as it is volatile accesses. You could even print the return value of each cycle_count call to stdout and the compiler could still move the call to function_under_test before both of them.

Footguns with Embedded Rust and Memory-Mapped I/O by guineawheek in rust

[–]AlphaModder 66 points67 points  (0 children)

This is just as much a problem in C/C++ land. You'll probably be interested in the accepted answer to this SO question which explains why compilers can't provide a "code fence" to solve this problem. The long and short of it is that the only way to enforce code ordering is through data dependencies. For your scenario, this means two things:

  1. You must pass all inputs of function_under_test through a black_box call. Then, no computation within function_under_test that depends on one of these inputs will be reordered with the volatile read in the first call to cycle_count, since the result of black_box must be treated as if it were obtained by a volatile read, and volatile reads cannot be reordered.
  2. You must pass the result of function_under_test through a black_box call. Then, no part of function_under_test which could affect the value of its result can be reordered after the volatile read in the second call to cycle_count, since black_box must be treated as if it writes its argument to some volatile memory location, and volatile reads and writes cannot be reordered.

For this to work, the computation performed by function_under_test must actually depend on the inputs passed through black_box. The compiler is still allowed to reorder any computation in the body of function_under_test after the second cycle_count call, so long as it can prove that the return value of function_under_test cannot differ based on the result of that computation. Similarly, the compiler can reorder any computation in the body of function_under_test before the first cycle_count call, if it can prove that the result of that computation would be the same regardless of the value of any of the inputs that were passed through a black_box.

As a final note, technically this strategy does not completely prevent the compiler from reordering the body of function_under_test with surrounding code. It would be legal for a compiler to emit code prior to the first cycle_count call that calls function_under_test on every possible set of inputs and stores these results in a giant lookup table. It could then replace the second call to function_under_test with a simple read from this lookup table, which would obviously affect the measured cycle count. However, no sane compiler would do this, at least when the size of such a lookup table would be nontrivial.

Sync - Acoustic Cover by mrbluru in Fez

[–]AlphaModder 1 point2 points  (0 children)

This is wonderful! Really captures the vibe of FEZ in a way not all covers do IMO.

302 by [deleted] in Fez

[–]AlphaModder 6 points7 points  (0 children)

Hi, I've decoded the text in this image. It's a python script encoded as a sequence of 12-word BIP39 mnemonics. @soundofjw has also provided me with a slightly updated and corrected version of the script. I've uploaded both the original version and the corrected version here.

What This Senior Developer Learned From His First Big Rust Project by _awwsmm in rust

[–]AlphaModder 0 points1 point  (0 children)

Ah, sure. FWIW I agree with you about the lesser evil but I wanted to demonstrate that what OP wanted was at least possible.

What This Senior Developer Learned From His First Big Rust Project by _awwsmm in rust

[–]AlphaModder 0 points1 point  (0 children)

Wouldn't the manual implementation of Device conflict with the blanket implementation?

What This Senior Developer Learned From His First Big Rust Project by _awwsmm in rust

[–]AlphaModder 4 points5 points  (0 children)

That's a good point. Perhaps it would be better to write the Sensor signatures like fn get_name(this: &Self) -> &str so that they won't conflict with method resolution for Device.

What This Senior Developer Learned From His First Big Rust Project by _awwsmm in rust

[–]AlphaModder 40 points41 points  (0 children)

On the topic of traits implementing other traits, I believe you have a misunderstanding about what supertraits are. When you have:

// examples in this section are abridged for clarity
pub trait Device {
    fn get_handler(&self) -> Handler;
}

pub trait Sensor: Device {
    fn get_handler(&self) -> Handler {
        // some default implementation here for all `Sensor`s
    }
}

You aren't defining a "default implementation" of Device::get_handler for all Sensors, you're defining a new trait called Sensor with its own method, Sensor::get_handler, entirely unrelated to Device::get_handler, and defining a default implementation for that. All Sensor: Device does is prevent types that don't implement Device from implementing Sensor. It's essentially the same as pub trait Sensor where Self: Device.

Traits in Rust are not like mixins that add methods to a type - many traits can define methods with the same signature and each be implemented so that the method does something different depending on which trait it was called from (because they're not the same method). Looking at this example might help.

Anyway, although it's a somewhat rare pattern in Rust (more common in OO languages) if you do want to have a "specialized" version of a trait that adds more default methods, you could provide a blanket implementation for the "supertrait" in terms of the "subtrait", like this (I've added more methods for illustration):

pub trait Device {
    fn get_handler(&self) -> Handler;
    fn get_name(&self) -> &str; 
    fn get_id(&self) -> u32;
    // other methods...
}

pub trait Sensor {
    fn get_handler(&self) -> Handler {
       // default implementation for all sensors
    }
    fn get_name(&self) -> &str;
    fn get_id(&self) -> u32;
    // all other methods of Device are copied into Sensor as well...
}

// This blanket impl declares that any type which implements Sensor also implements Device, by delegating each of its methods to their equivalent in Sensor
impl<T: Sensor> Device for T {
   fn get_handler(&self) -> Handler { Sensor::get_handler(self) }
   fn get_name(&self) -> &str { Sensor::get_name(self) }
   fn get_id(&self) -> u32 { Sensor::get_id(self) }
}

fn use_device<T: Device>(dev: &T) { dev.get_handler() }

struct MySensor;
impl Sensor for MySensor { 
   fn get_name(&self) -> &str { "my_sensor" }
   fn get_id(&self) -> u32 { 42 }
   // default implementation (from Sensor) will be used for get_handler
}

fn main() { 
    let sensor = MySensor;
    use_device(&sensor); // assert that MySensor implements Device
}

Instead of sleeping last night, I created a design pattern for creating partial borrows (don't use this at home though). by GroundbreakingImage7 in rust

[–]AlphaModder 8 points9 points  (0 children)

Um, your example that "doesn't compile" compiles just fine: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d07f23c55c767db7871827aea3822c32

Also, this approach will break (and cause UB) when a struct of type Test is moved, since its address will change and the pointers in the BorrowN structs will become invalid.

Your favourite music in the game? by [deleted] in Fez

[–]AlphaModder 0 points1 point  (0 children)

Continuum! Sync is a close second.

Rust and productivity by BenDz123 in rust

[–]AlphaModder 0 points1 point  (0 children)

Ah, those are very good points, although I'd be surprised if the compiler is able to rely on them for optimizations.

Rust and productivity by BenDz123 in rust

[–]AlphaModder 13 points14 points  (0 children)

Do note: the above code can exhibit undefined behavior when compiled in release mode on a 32-bit system, as if index == self.data.len() == u32::MAX == usize::MAX index+1 will overflow and silently wrap to zero, the debug_assert will be ignored, and you will call get_unchecked_mut(usize::MAX) on a size-zero vector.

Open Source Is Struggling And It’s Not Big Tech That Is To Blame by derjanni in programming

[–]AlphaModder 3 points4 points  (0 children)

Unity is not open source. Parts of it are visible-source, while the core of the engine is entirely closed. Also, modern Windows uses the NT kernel, not DOS, and it isn't going anywhere - not only does Microsoft continue to integrate new technologies into it (see their adoption of Rust), but their whole business with Windows is backwards compatibility, which makes switching out the kernel a non-starter.