Ensuring Memory Safety for Self-Referential Data with `Pin`
Owner: SnippetBot
Created: 2026-08-08 00:00:26
Size: 2.28 KB
Expires: Never
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
use std::marker::PhantomPinned;
use std::pin::Pin;
use std::ptr::NonNull;
// A struct that intends to hold a pointer to itself.
// This is usually problematic because the struct might be moved,
// invalidating the internal pointer.
#[derive(Debug)]
struct SelfReferential {
value: String,
// This field points to 'value' within the same struct.
// NonNull is used because we know it won't be null, but it's unsafe to dereference.
// This will be set after the struct is in stable memory.
ptr_to_value: Option<NonNull<String>>,
// PhantomPinned tells the compiler that this type is !Unpin,
// meaning it cannot be moved after it's pinned.
_pin: PhantomPinned,
}
impl SelfReferential {
// A safe constructor that returns a Pin<Box<SelfReferential>>
// to ensure the struct is allocated on the heap and pinned immediately.
fn new(val: String) -> Pin<Box<Self>> {
let mut s = Box::pin(SelfReferential {
value: val,
ptr_to_value: None,
_pin: PhantomPinned,
});
// Unsafely initialize the self-referential pointer.
// This is safe ONLY because `s` is already pinned and guaranteed not to move.
let ptr = NonNull::from(&s.value);
unsafe {
// We need to dereference the Pin<Box<SelfReferential>> to get a mutable reference
// to SelfReferential, then set the field. This is okay because we're not moving s.
let mutable_ref: Pin<&mut SelfReferential> = s.as_mut();
Pin::get_unchecked_mut(mutable_ref).ptr_to_value = Some(ptr);
}
s
}
// A safe method to get the value via the internal pointer.
fn get_value_from_ptr(self: Pin<&Self>) -> &str {
// This is safe because `self` is pinned, guaranteeing `ptr_to_value` is valid.
let ptr = self.ptr_to_value.unwrap().as_ptr();
unsafe { &*ptr }
}
}
fn main() {
let my_struct = SelfReferential::new("Hello from pinned data!".to_string());
println!("Struct value: {}", my_struct.value);
println!("Value via internal pointer: {}", my_struct.get_value_from_ptr());
// Attempting to move `my_struct` (e.g., `let new_struct = *my_struct;`)
// would result in a compile-time error because it is `!Unpin`.
// Pin prevents such memory unsafety.
}