Live data from Hacker News

Uninitialized memory: Unsafe Rust is too hard

lucumr.pocoo.org

91–100 of 127 posts

Re: Uninitialized memory: Unsafe Rust is too hard

#91
post #85
post #74

Earlier quoted context omitted.

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

RefCell is distinct from Cell, more common, but much worse tradeoffs for implicit access, because accessing it can crash your program.

I want Cell to be more ergonomic because it addresses the same Rust weak points as RefCell but without runtime overhead and panicking. I think RefCell should be an infrequently used type that you reach for when you specifically want to guard against reentrancy, and struct{Cell} should be the primary replacement for shared access in other languages. The latter is sound but introduces a lot of boilerplate syntax.

Re: Uninitialized memory: Unsafe Rust is too hard

#92

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 t…

If struct fields weren't aligned, then safe Rust would be completely and utterly broken. And really obviously so.

This isn't just a matter of things "seeming". It is quite literally an implication of Safe Rust works => field offsets must be aligned. There is no other way for safe Rust to be safe, other than alignment not mattering at all because all accesses are careful to pessimistically not rely on it.

I am sorry, but this hypothesis is just completely outside the Overton Window.

Re: Uninitialized memory: Unsafe Rust is too hard

#93
post #46

Earlier quoted context omitted.

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…

I don't buy into editions, for me I hardly see them any different from language version switches available across programming languages.

Mainly, because:

1 - They require the whole code, including all third party dependencies to be available to the compiler;

2 - There is the issue about possible inconsistencies across Rust compilers, when they start to be more widespread;

3 - They are relatively constrained the scope, e.g. semantic differences across versions and how that can be handled across crates public API

Let's see how editions take care about the many items that are yet to stabilise.

Naturally Rust being 30 years younger (approximately), it will always get less cruft.

Maybe by 2045 it can manage to do the same play C++ did on C, and there will be a LLVM replacement in Rust, while at the same time, there will be domains that regardless how much cruft C++ has gained, they will continue to use, just like it almost impossible to take C out of UNIX clones and embedded, no matter how much C++ has tried.

Re: Uninitialized memory: Unsafe Rust is too hard

#94
But the code isn't equivalent. The C code just has a pointer to a manually allocated buffer, while the Rust does the equivalent of zeroing out (or leaving uninitialized) a C++ std::string. Akin to:

auto name = reinterpret_cast>(malloc(sizeof(std::string))); memset(name, 0, sizeof(std::string); *name = "basic";

But on the stack.

Re: Uninitialized memory: Unsafe Rust is too hard

#95

Earlier quoted context omitted.

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

You need to put something in those braces. An empty initializer list is not allowed by C99 or C11. That being said, you only need to put ONE thing in there, the rest will then be initialized as you described.

Re: Uninitialized memory: Unsafe Rust is too hard

#96

Earlier quoted context omitted.

I guess because it's off topic. TFA is about a specific Rust issue and a generic comment about "Rust is hard to learn" doesn't add anything to the conversation. It's like the generic "C++ is way to complicated" comment under every C++ post. They just incite language flame wars and district from the actually interesting stuff.

It's because, for some reason, the rust community is extremely, overly aggressive about downvoting. I think we can all just admit that - I've been using Rust since 2014 and it's always been an issue and it needs to be called out.

I haven't commented/been upvoted enough to be allowed to downvote, and it is honestly quite liberating and lets me focus on elevating what I think are positive contributions.

Re: Uninitialized memory: Unsafe Rust is too hard

#97
post #73

Earlier quoted context omitted.

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.

That should work perfectly with multiple structs. Define

    #[repr("C")]
    struct MyThing {
        length: usize,
        data: MaybeUninit
    }
Then initialize with sizeof(data) and MaybeUninit::uninitialized(). When the call is complete, assume_init() and access the fields of the result struct as normal.

Re: Uninitialized memory: Unsafe Rust is too hard

#98

Earlier quoted context omitted.

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 t…

If struct fields weren't aligned, then safe Rust would be completely and utterly broken. And really obviously so. This isn't just a matter of things "seeming". It is quite literally an implication of Safe Rust works => field offsets must be aligned. There is no other way for safe Rust to be safe, other than alignment not mattering at all because all accesses are careful to pessimistically not rely on it. I am sorry,…

The thing is, C has an actual standard and people get bit making the same kinds of assumptions on things not stated. You may very well be right that there's no way this will ever happen, but then it also doesn't appear to be guaranteed. They literally say they make no guarantees about the data layout, but you're assuming there is a guarantee about the data layout :P

Maybe this time it works out, but there's plenty of other cases where things are not so clear cut.

Re: Uninitialized memory: Unsafe Rust is too hard

#99
post #93

Earlier quoted context omitted.

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…

I don't buy into editions, for me I hardly see them any different from language version switches available across programming languages. Mainly, because: 1 - They require the whole code, including all third party dependencies to be available to the compiler; 2 - There is the issue about possible inconsistencies across Rust compilers, when they start to be more widespread; 3 - They are relatively constrained the scope…

(All responding to 1) A. Binary dependencies were a mistake. B. No, editions absolutely do not require all dependencies to be fully available in source. Editions are purely a front end thing and as long as you have enough info for the api/abi, you're fine. C. Headers are still source code. You cannot use any dependency without partial source, meaning we'd be stuck with that anyway.

Re: Uninitialized memory: Unsafe Rust is too hard

#100
post #68
post #58

Earlier quoted context omitted.

The article says > A mutable reference must also never point to an invalid object, so doing let role = &mut *uninit.as_mut_ptr() if that object is not fully initialized is also wrong. I'm curious who's right here, because I've seen your pattern in code recently!

Aren't all the `(*role)` in their code "mutable references" too? (*role).name = "basic";

No, this is specifically not creating a mutable reference. So it's fine except for the fact that writing via assignment like this calls the destructor/drop for the old value, if the type has a Drop implementation. That's why &str is fine but String is not, since it will call Drop on a zeroed String (Which is not guaranteed to work) or on an uninitialized String. Using ptr::write instead explicitly does not Drop the old value that is being overwritten.

Basically, if you have something like

    let v = vec![1,2,3];
    v = vec![4,5,6]; // Here vec![1,2,3] is dropped otherwise we would be leaking
Post reply on HN