Live data from Hacker News

Uninitialized memory: Unsafe Rust is too hard

lucumr.pocoo.org

71–80 of 127 posts

Re: Uninitialized memory: Unsafe Rust is too hard

#71

I think that the premise here is correct - writing unsafe Rust is too hard. There are lots of footguns. This isn't a very good motivating example but I suppose it does the job of showing the various hoops one has to jump through when using unsafe. I think right now the approach is to make unsafe "safe" (ie std::mem::uninitialized -> MaybeUninit) at the cost of complex, and eventually to build out improved helpers and…

When I want to write unsafe Rust code, I usually find it easier just to use C. The whole purpose for Rust in my work is to be safe.

Re: Uninitialized memory: Unsafe Rust is too hard

#72

The scary thing is: Handling uninitialized memory is hard in C++ (and C), too. You just don't notice and accidentally do it slightly wrong (mainly in C++, in C it's harder to mess up).

Exactly this. And also avoiding initializing memory with zeroes or something is often premature optimization. Very few programs are performant enough to notice the difference.

Re: Uninitialized memory: Unsafe Rust is too hard

#73
post #35

Earlier quoted context omitted.

A much better way to do partial initialization is by splitting up the struct into multiple parts. This can be easily done in safe rust with Option, or MaybeUninit if you're really desperate for performance.

When dealing with unix syscalls, you actually sometimes need to pass structs that aren't fully initialized, or are initialized to zero with the exception of some fields. The quintessential example is the sigaction struct.

Another good example is Win32, in many cases only the length is initialized and the API does the rest, this allows them to change the ABI across versions without impacting the caller.

Re: Uninitialized memory: Unsafe Rust is too hard

#74
post #70

Earlier quoted context omitted.

If Rust's syntax is geared to making safe Rust ergonomic, they should start by not requiring method calls to read or write from a &Cell (because in C++ you don't need method calls to read or write from a T&).

Needing to use a `Cell` in rust is incredibly rare in my experience. It's very far from clear to me that the increased complexity coming from allowing overriding the meaning of `=`, and worse of `variable_name_that_happens_to_contain_a_certain_type` would be remotely worth it.

Unless GUI code comes up with Rc> all over the place.

Re: Uninitialized memory: Unsafe Rust is too hard

#76
post #5

In C, when you declare 'struct role r' (not as a static variable), it is not zeroed. The immediate Rust equivalent would be to use std::mem::uninitialized(), not std::mem::zeroed.

But an idiom from C which might inspire some unsafe rust is to memset a struct to zero after declaration in order to guarantee that all fields are initialized before anything would access them.

If I understand correctly, reading [0], in C99 (or later) you can do that with

    struct MyStruct foo = {};
This has the effect of initializing all members to zero (or, more precisely, the value which is the same as for objects that have static storage duration [0]).

[0] https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html

See also Stop Memsetting Structures: https://news.ycombinator.com/item?id=19766930

Re: Uninitialized memory: Unsafe Rust is too hard

#77
post #46

Earlier quoted context omitted.

I much prefer Rust over C++, but I find that the problems of C++ have little to do with the language itself. I've been watching the videos by Andreas King on SerenityOS and the code is so clean that at first I wondered what programming language I was even looking at. I see the SerenityOS codebase as proof that if C++ programmers wanted to write modern, elegant, readable code, they definitely could. In practice, thoug…

Having acquired C++ into my toolbox in 1993, and thus lived through its adoption over C (which still owns several domains after 50 years), I am bettting that Rust at 30 years of age into production will suffer similar fate.

Even though Rust's Editions don't solve everything they make a huge difference and they also change the nature of the conversation around such evolution.

I think the built-in array type is illustrative. In both languages (C++ and Rust) the initial 1.0 language offers a built in array type that is provided with built-in syntax and parsing but isn't as good as the user-made container types, so on day one the situation is OK, yeah, we do have arrays but you should likely avoid them.

In C++ that just remains the case, C++ 20 has poor built-in arrays and a note saying we built another array type that you should actually use, it's in our standard library.

Meanwhile in Rust they've been improving their built-in arrays, using const generics, implementing IntoIterator for arrays, and so on. Rust 2021 in a compiler today has pretty nice built-in arrays that behave how you'd expect for a container, a sophisticated programmer might notice that Default isn't implemented for your array of 64 integers, but such sharp corners are now few and far between and further refinements continue.

The resulting conversation is more open to change, even though Editions can't actually do magic they can conceal some pretty deep compiler magic like the hack to enable IntoIterator for arrays yet keep working Rust 2015 and Rust 2018 code that assumed into_iter() on an array will go via a reference. Being able to get to 90% of what people wanted with no magic meant the conversation about extra magic happened and it might otherwise not have.

Editions also spur language innovations that make further edition work easier. Rust 1.0 did not have any way to talk about an identifier if it collided with a keyword, which of course means if you reserve a new keyword now you can't access identifiers which used the now-reserved name. Rust 2018 introduces raw identifiers to fix that, if you really insist on naming your function "try" you can write r#try despite the existence of the keyword try.

I think these benefits are cumulative, and although Rust 2045 might have some cruft it will have a lot less than C++ 23 let alone C++ 44.

Re: Uninitialized memory: Unsafe Rust is too hard

#78
post #63

Earlier quoted context omitted.

Exactly this. I'm not sure what the author's practical goal is with that code. He rejects #[repr(C)] a few times, so it's not FFI. Yes, working with uninitialized memory is tedious. But that isn't something you ever have to do. If you're translating some C to Rust, write it using Rust idioms, instead of trying to preserve every call to malloc/free and every access to uninitialized memory.

The point is to make points in a simple environment. Anything small enough to clearly make points about unsafe Rust is almost certainly small enough to be done in safe Rust, defeating the purpose.

If it’s too big for an example, it’s almost certainly too big for “trust me, I know this is safe”.

Re: Uninitialized memory: Unsafe Rust is too hard

#79

The write_unaligned is pure FUD. Regular unpacked structs don't violate alignment on fields!

I agree, but IMO it's not all that clear to me that this is guaranteed. The documentation for the default layout pretty clearly says[0]:

> There are no guarantees of data layout made by this representation.

So that being the case, there's not really anything stopping them from introducing a situation where an unaligned field in a struct is created in the future. Of course I can't imagine why they would do that, but then maybe my imagination just isn't good enough. I think the author's point here (which is a good one in my opinion) is that when writing `unsafe` you're not supposed to rely on stuff that seems like it should be true, you're supposed to rely on stuff that's guaranteed to always be true, which with Rust isn't all that clearly defined.

[0]: https://doc.rust-lang.org/reference/type-layout.html#the-def...

Re: Uninitialized memory: Unsafe Rust is too hard

#80

Earlier quoted context omitted.

> But also, just don't write unsafe? It's very easy to avoid. Yeah, there's a weird subset of developers who insist on mixing unsafe and safe code even when they're presented equally performant, safe alternatives. One such example was the Actix framework, where the lead dev refused to merge any fixes for his unsafe code. Eventually, so many merge requests showed up to fix his broken code that he just gave up the proj…

> If you want to write unsafe code, I think that's perfectly fine, but Rust is not going to cater to your desires. C and C++ will give you the tools you need with the conveniences you want. This is a bit of a odd suggestion to me, honestly, you're basically saying Rust is not intended to be a C/C++ replacement. There is definitely a reality that not everything can be written in completely safe Rust, and a lot of the…

Calling Rust "a C/C++ replacement" is, I think, slightly underselling what it's supposed to do. As I understand it, the goal of Rust is more "take all these things that were historically hard to do safely and make them easier to do safely" than "replace C/C++ 1 for 1".
Post reply on HN