Open
Description
use core::cell::UnsafeCell;
use zerocopy::AsBytes;
// INVARIANT: A pointer to `Self` is never created with `SharedReadWrite`
// permissions.
#[repr(transparent)]
pub struct ReadOnly<T>(T);
impl<T> ReadOnly<T> {
fn new(t: T) -> ReadOnly<T> {
ReadOnly(t)
}
}
impl<T> ReadOnly<T> {
fn from_mut<'a>(t: &'a mut T) -> &'a mut ReadOnly<T> {
let t_ptr: *mut T = t;
// INVARIANT: Because `t: &mut T`, `t_ptr` has `Unique` permissions, not
// `SharedReadWrite` permissions. This is true even if `T` contains
// `UnsafeCell`s.
unsafe { &mut *t_ptr.cast::<ReadOnly<T>>() }
}
}
impl<T: AsBytes> ReadOnly<UnsafeCell<T>> {
fn as_bytes(&self) -> &[u8] {
let len = size_of_val(self);
let slf: *const Self = self;
// SAFETY: Since, by invariant, a pointer to `Self` is never constructed
// with `SharedReadWrite` permissions, we know that `self` must only
// have `Shared` permissions. Thus, this operation does not invalidly
// re-tag the pointer's permissions.
//
// Since `T: AsBytes` and since `UnsafeCell<T>` has the same bit
// validity as `T`, none of the bytes of `*self` are uninitialized.
unsafe { core::slice::from_raw_parts(slf.cast::<u8>(), len) }
}
}
fn main() {
// By value
let ro = ReadOnly::new(UnsafeCell::new(0u8));
println!("{:?}", ro.as_bytes());
// By reference
let mut val = UnsafeCell::new(0u8);
let ro = ReadOnly::from_mut(&mut val);
println!("{:?}", ro.as_bytes());
}
The key idea is that, while Stacked Borrows can't be used to disable UnsafeCell
s if you've started out with a &UnsafeCell
, which has SharedReadWrite
permissions (see rust-lang/unsafe-code-guidelines#303), it's still sound so long as you never create a pointer with SharedReadWrite
permissions in the first place. You can do that by only constructing a ReadOnly
by value or by &mut
.
Metadata
Metadata
Assignees
Labels
No labels