{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreico5xtcyxayrnspiucbi2fy236lehbairzb76hm52kiinap2ai5wu",
"uri": "at://did:plc:ivbknywyskln22er3nkssdhl/app.bsky.feed.post/3mgeaf3wm5jv2"
},
"path": "/t/interior-mutability-and-safety-of-ownership-transfer-in-rust/24055#post_8",
"publishedAt": "2026-03-05T21:11:30.000Z",
"site": "https://internals.rust-lang.org",
"textContent": "increasing:\n\n> If i understand correctly this is enough here, as any data behind an indirection won't be copied by `ptr::read/write` and so won't be duplicated during the reallocation.\n\nYes, to me that seems to be the case. \"Shallow\" interior mutability is a problem that can lead to data races, but interior mutability behind an indirection layer should be fine _if_ the duplication of values is in itself not unsound.\n\nBut it seems my description was not as clear as I had thought it was, so I'll try to explain it a bit better (hopefully).\n\nSo, a simplified version of the buffer type could look like this:\n\n\n struct Buffer<K, V> {\n len: usize,\n pairs: [(K, V)], // Note: This field makes Buffer a DST!\n }\n\n\nA pseudo-code function for growing a buffer would roughly look like this:\n\n\n struct HashMap<K, V> {\n buf: AtomicPtr<Buffer<K, V>>,\n rcu: Rcu,\n }\n\n impl<K, V> HashMap<K, V>\n where\n K: Hash + Eq + Freeze,\n V: Freeze,\n {\n unsafe fn grow_buffer(&self) {\n let old: &Buffer<K, V> = &*self.buf.load(Ordering::Acquire);\n let new: *mut Buffer<K, V> = Buffer::alloc_buffer(old.len * 2);\n\n // semantically \"move\" all (K, V) from old to new\n for i in 0..old.len {\n let src: *const (K, V) = &old.pairs[i] as _;\n // get a raw pointer to the (K,V) pair through a raw pointer\n let dst: *mut (K, V) = &raw mut (*new.pairs[i]);\n // (K, V) exists in old and new at the same time!\n // readers can still access `old` and get shared references\n // into it.\n dst.write(src.read());\n }\n\n // after this swap *new* readers will see the same values that exist\n // in `old` but at a different memory location (only shared references)\n let _ = self.buf.swap(new, Ordering::Release);\n // This will block until no reader that started before the atomic swap is\n // accessing `old` any more, i.e., there can no longer be *any* shared\n // references into `old`.\n self.rcu.synchronize();\n // ... since there are no longer any references, `old` can be freed.\n // this call frees the buffer's memory without dropping any `(K, V)`.\n Buffer::<K, V>::free(old as *mut Buffer<K, V>);\n }\n }\n\n\nI hope this pseudo code is maybe a little easier to follow than my initial description. I doubt this would compile but it's close enough to what the real code would look like.\n\nOf course, the actual code would be significantly more complex with lots of `UnsafeCell`s and `MaybeUninit`s sprinkled throughout.",
"title": "Interior mutability and safety of ownership transfer in Rust"
}