-
Notifications
You must be signed in to change notification settings - Fork 205
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Clarify safety properties of casts between [MaybeUninit<T>]
<-> [T]
.
#431
Conversation
669b857
to
215d402
Compare
slice::from_raw_parts[_mut]()
.[MaybeUninit<T>]
<-> [T]
.
fd6209d
to
017a7db
Compare
src/util.rs
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 }
src/util.rs
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 }
src/util.rs
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps we should update this to be:
The caller ensures that the return slice will never be uninitialized.
src/util.rs
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
d11265a
Compare
Reduce the scope of the `unsafe` blocks to the unsafe operations.
d11265a
to
534fd68
Compare
I updated this with the following changes:
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the fixes @briansmith. Agreed w.r.t. to the shorter SAFETY
comments
Reduce the scope of the
unsafe
blocks to the unsafe operations.