Without any unsafe code this is simply: let role = Role { name: "basic", flag: 1, disabled: false, }; The language tries to prevent you from interacting with a `Role` object that's not fully initialized. `mem::zero()` could work, but then you'll have to turn the `&'static str` into an `Option ` or a raw pointer, to indicate that it might be null. You could also add `#[derive(Default)]` to the struct, to automatically…
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.
Uninitialized memory: Unsafe Rust is too hard
51–60 of 127 posts
Re: Uninitialized memory: Unsafe Rust is too hard
#52Without any unsafe code this is simply: let role = Role { name: "basic", flag: 1, disabled: false, }; The language tries to prevent you from interacting with a `Role` object that's not fully initialized. `mem::zero()` could work, but then you'll have to turn the `&'static str` into an `Option ` or a raw pointer, to indicate that it might be null. You could also add `#[derive(Default)]` to the struct, to automatically…
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.
Re: Uninitialized memory: Unsafe Rust is too hard
#53Without any unsafe code this is simply: let role = Role { name: "basic", flag: 1, disabled: false, }; The language tries to prevent you from interacting with a `Role` object that's not fully initialized. `mem::zero()` could work, but then you'll have to turn the `&'static str` into an `Option ` or a raw pointer, to indicate that it might be null. You could also add `#[derive(Default)]` to the struct, to automatically…
const struct role r = {
.name = "basic",
.flag = 1,
.disabled = false,
};
(of course this doesn't give you uninitialized memory in case new items are added to the struct, but why would one ever want that?)Re: Uninitialized memory: Unsafe Rust is too hard
#54I 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…
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 project altogether and let the community take over.
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.
Re: Uninitialized memory: Unsafe Rust is too hard
#55Re: Uninitialized memory: Unsafe Rust is too hard
#56Absolutely not, you can still use a mutable reference:
let role = &mut *uninit.as_mut_ptr();
role.name = "basic";Re: Uninitialized memory: Unsafe Rust is too hard
#57Where is this coming from? It's literally not true. The MIR for this has:
((*_3).0: &str) = const "basic";
((*_3).2: u32) = const 1_u32;
((*_3).1: bool) = const false;
So it's only going to do a raw offset and then assign to it, which is identical to `*ptr::addr_of_mut!((*role).field) = value`.Sadly there's no way to tell miri to consider `&mut T` valid only if `T` is valid (that choice is not settled yet, AFAIK, at the language design level), in order to demonstrate the difference (https://github.com/rust-lang/miri/issues/1638).
The other claim, "dereferencing is illegal", is more likely, but unlike popular misconception, "dereference" is a syntactic concept, that turns a (pointer/reference) "value" into a "place".
There's no "operation" of "dereference" to attach dynamic semantics to. After all, `ptr::addr_of_mut!(*p).write(x)` has to remain as valid as `p.write(x)`, and it does literally contain a "dereference" operation (and so do your field projections).
So it's still inaccurate. I believe what you want is to say that in `place = value` the destination `place` has to hold a valid value, as if we were doing `mem::replace(&mut place, value)`. This is indeed true for types that have destructors in them, since those would need to run (which in itself is why `write` on pointers exists - it long existed before any of the newer ideas about "indirect validity" in recent years).
However, you have `Copy` types there, and those are definitely not different from `::write` to assign to, today. I don't see us having to change that, but I'm also not seeing any references to where these ideas are coming from.
> I'm pretty sure we can depend on things being aligned
What do you mean "pretty sure"? Of course you can, otherwise it would be UB to allow safe references to those fields! Anything else would be unsound. In fact, this goes hand in hand with the main significant omission of this post: this is not how you're supposed to use `MaybeUninit`.
All of this raw pointer stuff is a distraction from the fact that what you want is `&mut MaybeUninit`. Then all of the things about reference validity are necessarily true, and you can safely initialize the value. The only `unsafe` operation in this entire blog post, that isn't unnecessarily added in, is `assume_init`.
What the author doesn't mention is that Rust fails to let you convert between `&mut MaybeUninit` and some hypothetical `&mut StructBut>` because the language isn't powerful enough to do it automatically. This was one of the saddest things about `MaybeUninit` (and we tried to rectify it for at least arrays).
This is where I was going to link to a custom derive that someone has written to generate that kind of transform manually (with the necessary check for safe field access wrt alignment). To my shock, I can't find one. Did I see one and did it have a funny name? (the one thing I did find was a macro crate but unlike a derive those have a harder time checking everything so I had to report https://github.com/youngspe/project-uninit/issues/1)
Re: Uninitialized memory: Unsafe Rust is too hard
#58> Because that raw pointer does not implement deref and because Rust has no -> operator we now need to dereference the pointer permanently to assign the fields with that awkward syntax. Absolutely not, you can still use a mutable reference: let role = &mut *uninit.as_mut_ptr(); role.name = "basic";
> 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!
Re: Uninitialized memory: Unsafe Rust is too hard
#59> Because that raw pointer does not implement deref and because Rust has no -> operator we now need to dereference the pointer permanently to assign the fields with that awkward syntax. Absolutely not, you can still use a mutable reference: let role = &mut *uninit.as_mut_ptr(); role.name = "basic";
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!
> Incorrect usage of this method:
let mut x = MaybeUninit::>::uninit();
let x_vec = unsafe { &mut *x.as_mut_ptr() };
// We have created a reference to an uninitialized vector! This is undefined behavior.
Also, above, it explicitly describes the intended API for partially initializing a struct: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.h...Re: Uninitialized memory: Unsafe Rust is too hard
#60Earlier 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!
The docs for as_mut_ptr: https://doc.rust-lang.org/stable/std/mem/union.MaybeUninit.h... > Incorrect usage of this method: let mut x = MaybeUninit:: >::uninit(); let x_vec = unsafe { &mut *x.as_mut_ptr() }; // We have created a reference to an uninitialized vector! This is undefined behavior. Also, above, it explicitly describes the intended API for partially initializing a struct: https://doc.rust-lang.org/stable/st…
It turns out I had misremembered; the cast I was thinking of is
https://github.com/oxidecomputer/hubris/blob/master/sys/kern...
from &mut MaybeUninit to &mut [MaybeUninit], which doesn't construct a reference to something uninitialized.