Uh oh!
There was an error while loading. Please reload this page.
Clarify safety properties of casts between [MaybeUninit<T>] <-> [T]. - #431
Conversation
669b857 to
215d402Compareslice::from_raw_parts[_mut]().[MaybeUninit<T>] <-> [T].fd6209d to
017a7dbCompare| let ptr: *mut [MaybeUninit<T>] = slice; | ||
| // SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T` | ||
| // and casting between two slice types of layout-compatible types is sound. | ||
| let ptr = ptr as *mut [T]; |
There was a problem hiding this comment.
I think let ptr = slice as *mut [MaybeUninit<T>] as *mut [T] would be a bit easier to understand than let ptr: *mut [MaybeUninit<T>] = slice.
There was a problem hiding this comment.
I agree doing it on one line is more reasonable, we also probably don't need the first SAFETY comment, as it confuses things. It is always legal to cast from a *mut [A] to a *mut [B] (where A and B are sized). It's dereferencing where we need to be sure that:
- The pointer is aligned
- The range
[ptr,ptr+len)is exclusive and valid for reads/writes - The bytes are initialized
So something like:
let ptr = slice as*mut[MaybeUninit<T>]as*mut[T];// SAFETY: As `MaybeUninit<T>` is layout compatible with `T`, `ptr` is// appropriately aligned and points to the same memory as `slice`. As `ptr`// was created from a valid `&mut` reference and the caller ensures every// element is initialized, it is safe to dereference `ptr`.unsafe{&mut*ptr }| let ptr: *mut [MaybeUninit<T>] = slice; | ||
| // SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T` | ||
| // and casting between two slice types of layout-compatible types is sound. | ||
| let ptr = ptr as *mut [T]; |
There was a problem hiding this comment.
I agree doing it on one line is more reasonable, we also probably don't need the first SAFETY comment, as it confuses things. It is always legal to cast from a *mut [A] to a *mut [B] (where A and B are sized). It's dereferencing where we need to be sure that:
- The pointer is aligned
- The range
[ptr,ptr+len)is exclusive and valid for reads/writes - The bytes are initialized
So something like:
let ptr = slice as*mut[MaybeUninit<T>]as*mut[T];// SAFETY: As `MaybeUninit<T>` is layout compatible with `T`, `ptr` is// appropriately aligned and points to the same memory as `slice`. As `ptr`// was created from a valid `&mut` reference and the caller ensures every// element is initialized, it is safe to dereference `ptr`.unsafe{&mut*ptr }| let ptr: *const [T] = slice; | ||
| // SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T` | ||
| // and casting between two slice types of layout-compatible types is sound. | ||
| let ptr = ptr as *const [MaybeUninit<T>]; | ||
| // SAFETY: | ||
| // * ptr was soundly constructed from a valid reference so it safe to | ||
| // dereference it. | ||
| // * There is no risk of writing a `MaybeUninit<T>` into the result | ||
| // since the result isn't mutable. | ||
| unsafe { &*ptr } |
There was a problem hiding this comment.
Perhaps:
let ptr = slice as*const[T]as*const[MaybeUninit<T>];// SAFETY: As `MaybeUninit<T>` is layout compatible with `T`, `ptr` is// appropriately aligned and points to the same memory as `slice`. As `ptr`// was created from a valid reference, any `T` is a valid `MaybeUninit<T>`,// and the return type is immutable, it is safe to dereference `ptr`.unsafe{&*ptr }| /// This is unsafe because it allows assigning uninitialized values into | ||
| /// `slice`, which would be undefined behavior. |
There was a problem hiding this comment.
Perhaps we should update this to be:
The caller ensures that the return slice will never be uninitialized.
| let ptr: *mut [T] = slice; | ||
| // SAFETY: `MaybeUninit<T>` is guaranteed to be layout-compatible with `T` | ||
| // and casting between two slice types of layout-compatible types is sound. | ||
| let ptr = ptr as *mut [MaybeUninit<T>]; | ||
| // SAFETY: | ||
| // * ptr was soundly constructed from a valid reference so it safe to | ||
| // dereference it. | ||
| // * There *IS* a risk of writing a `MaybeUninit<T>` into the result, | ||
| // which is why this function is unsafe. | ||
| unsafe { &mut *ptr } |
There was a problem hiding this comment.
Then this implementation can be:
let ptr = slice as*mut[T]as*mut[MaybeUninit<T>];// SAFETY: As `MaybeUninit<T>` is layout compatible with `T`, `ptr` is// appropriately aligned and points to the same memory as `slice`. As `ptr`// was created from a valid `&mut` reference, any `T` is a valid// `MaybeUninit<T>`, and the caller ensures the returned reference will not// be uninitialized, it is safe to dereference `ptr`.unsafe{&*ptr }017a7db to
d11265aCompareReduce the scope of the `unsafe` blocks to the unsafe operations.
d11265a to
534fd68Comparebriansmith
commented
May 28, 2024
I updated this with the following changes:
|
josephlr
left a comment
There was a problem hiding this comment.
Thanks for the fixes @briansmith. Agreed w.r.t. to the shorter SAFETY comments
…]`. (rust-random#431) Reduce the scope of the `unsafe` blocks to the unsafe operations.
Reduce the scope of the
unsafeblocks to the unsafe operations.