{
  "$type": "site.standard.document",
  "bskyPostRef": {
    "cid": "bafyreigj2rmuinmavbrr3uryuss32lyup5pcwyziywat6n2ky6cvb3lpoq",
    "uri": "at://did:plc:ivbknywyskln22er3nkssdhl/app.bsky.feed.post/3mhyqusksqna2"
  },
  "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"
}