{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreigj2rmuinmavbrr3uryuss32lyup5pcwyziywat6n2ky6cvb3lpoq",
"uri": "at://did:plc:ivbknywyskln22er3nkssdhl/app.bsky.feed.post/3mi3ideh3dc62"
},
"path": "/t/on-replacing-unsafecell/24113#post_4",
"publishedAt": "2026-03-26T20:44:22.000Z",
"site": "https://internals.rust-lang.org",
"textContent": "\n struct Bar { #[twilight] value: i32 }\n struct BarLike { value: UnsafeCell<MaybeUninit<i32>> }\n\n\nI might've phrased it poorly, but Bar is mostly similar to BarLike, except that...\n`&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.\n\n> It's worth noting that there's no significant difference in practice between `&UnsafeCell<T>` and `*mut T`:\n\nAt 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.\n\n* * *\n\n> How would this handle _safe_ cells, like `Cell` and `Mutex`?\n\n\n struct Mutex<T: ?Sized> {\n lock_state: AtomicU64,\n #[twilight] value: T\n }\n impl<T: ?Sized> Mutex<T> {\n // whatever machinery is used for locking...\n // and also\n pub fn get_mut(&mut self) -> &mut T {\n let value_location: NonNull<MaybeUninit<T>> = self.value;\n\n // **Our type** defines this projection in non-shared state is sound,\n // and that contents are always initialized.\n unsafe { value_location.as_mut().assume_init_mut() }\n }\n }\n\n\nUnsafeCell can also be remade with one twilight field and one extra projection - mutable cell reference to mutable contents reference. Every kind of cell can.\n\nThe 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.\n\n> I think `ManuallyDrop` is unrelated (`ManuallyDrop<Box<T>>` causes problems because you can't move a dropped `Box`, but in that scenario, you are accessing by value not by reference)\n\nPoint taken; I see that ManuallyDrop forwards niches and so it indeed cannot be replaced with MaybeUninit.\n\nFor 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.\n\n## Some thoughts on sugar\n\n * `#[twilight(always_init)]` - does not have that automatic MaybeUninit;\n * `#[twilight(always_init, mut_to_mut)]` - the UnsafeCell, and also std::sync::Exclusive;\n * `#[twilight(always_init, ref_to_ref, mut_to_mut)]` - effectively the same as a lit field.\n\n\n\nThanks! 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.\nI 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.",
"title": "On replacing UnsafeCell"
}