On replacing UnsafeCell
struct Bar { #[twilight] value: i32 }
struct BarLike { value: UnsafeCell<MaybeUninit<i32>> }
I might've phrased it poorly, but Bar is mostly similar to BarLike, except that...
&mut Bar still gives NonNull<MaybeUninit<i32>> and not &mut MaybeUninit<i32>; code needs to decide when dereferencing the pointer is safe, and it might not be if the borrow was sent somewhere else.
It's worth noting that there's no significant difference in practice between
&UnsafeCell<T>and*mut T:
At this point I notice that NonNull<i32> and &UnsafeCell<i32> are a bit different - the latter implies there is certainly a (4-aligned) allocation while the former might be dangling or unaligned. I'm not sure if it matters.
How would this handle safe cells, like
CellandMutex?
struct Mutex<T: ?Sized> {
lock_state: AtomicU64,
#[twilight] value: T
}
impl<T: ?Sized> Mutex<T> {
// whatever machinery is used for locking...
// and also
pub fn get_mut(&mut self) -> &mut T {
let value_location: NonNull<MaybeUninit<T>> = self.value;
// **Our type** defines this projection in non-shared state is sound,
// and that contents are always initialized.
unsafe { value_location.as_mut().assume_init_mut() }
}
}
UnsafeCell can also be remade with one twilight field and one extra projection - mutable cell reference to mutable contents reference. Every kind of cell can.
The hope is that this primitive is more composable and replaces several features at once. A drawback is that mutable references now might have a disconnected read-write span - oops, std::mem::swap is still a problem - and that provenance for twilight field's pointer needs to be sourced from somewhere instead of derived from the original reference.
I think
ManuallyDropis unrelated (ManuallyDrop<Box<T>>causes problems because you can't move a droppedBox, but in that scenario, you are accessing by value not by reference)
Point taken; I see that ManuallyDrop forwards niches and so it indeed cannot be replaced with MaybeUninit.
For why I mean twilight fields to be MaybeUninit, I see "slot is potentially uninitialized" vs "slot is potentially used by other code/other threads for whatever purposes" as close enough.
Some thoughts on sugar
#[twilight(always_init)]- does not have that automatic MaybeUninit;#[twilight(always_init, mut_to_mut)]- the UnsafeCell, and also std::sync::Exclusive;#[twilight(always_init, ref_to_ref, mut_to_mut)]- effectively the same as a lit field.
Thanks! Feedback's why I put the idea in the open; I'll still look if this perspective of looking on custom-projectable types has some intuitive semantics.
I continue to hold that UnsafeCell<T> is some strange "type" because it doesn't do anything in its owned form, and is rather pointless to pass through function boundaries in any form.
Discussion in the ATmosphere