There is **no safe way** to create self-referential structs in Rust without `unsafe` or a helper crate. This is by design — the borrow checker cannot express "this field borrows from that field" because moves would invalidate the pointer. **Why `Pin` doesn't solve it alone:** `Pin` prevents the *consumer* from moving the value, but it doesn't help the compiler understand the self-referential borrow. You still need `unsafe` to create and dereference the raw pointer. **Practical options, ranked:** 1. **`ouroboros` crate (recommended):** ```rust use ouroboros::self_referencing; #[self_referencing] struct MyStruct { data: String, #[borrows(data)] #[covariant] slice: &'this str, } ``` Generates safe accessors backed by `unsafe` internally. Battle-tested, widely used. 2. **`self_cell` crate:** ```rust use self_cell::self_cell; self_cell! { struct MyStruct { owner: String, #[covariant] dependent: Slice, } } type Slice = &'a str; ``` Simpler API than ouroboros, slightly less flexible. 3. **Redesign to avoid self-reference:** - Store an index instead of a reference: `ptr: usize` indexing into `data` - Split into two structs with explicit lifetime: `struct View { data: &'a String }` - Use `Rc` so both "fields" share ownership without borrowing 4. **Manual `unsafe` with `Pin`:** ```rust impl SelfRef { fn new(data: String) -> Pin> { let mut boxed = Box::pin(SelfRef { data, ptr: std::ptr::null(), _pin: PhantomPinned }); let ptr = &boxed.data as *const String; unsafe { boxed.as_mut().get_unchecked_mut().ptr = ptr; } boxed } } ``` Works but you own the `unsafe` — any future refactoring that moves the struct is UB. **Bottom line:** Use `ouroboros` or redesign to avoid the self-reference entirely. The index-based approach (option 3) is often the simplest and most Rust-idiomatic solution.
fe22ac4f-ba20-4ba8-9b32-0640aada50f4
There is no safe way to create self-referential structs in Rust without unsafe or a helper crate. This is by design — the borrow checker cannot express "this field borrows from that field" because moves would invalidate the pointer.
Why Pin doesn't solve it alone:
Pin prevents the consumer from moving the value, but it doesn't help the compiler understand the self-referential borrow. You still need unsafe to create and dereference the raw pointer.
Practical options, ranked:
ouroboroscrate (recommended):
use ouroboros::self_referencing;
#[self_referencing]
struct MyStruct {
data: String,
#[borrows(data)]
#[covariant]
slice: &'this str,
}Generates safe accessors backed by unsafe internally. Battle-tested, widely used.
self_cellcrate:
use self_cell::self_cell;
self_cell! {
struct MyStruct {
owner: String,
#[covariant]
dependent: Slice,
}
}
type Slice = &'a str;Simpler API than ouroboros, slightly less flexible.
Redesign to avoid self-reference:
- Store an index instead of a reference:
ptr: usizeindexing intodata - Split into two structs with explicit lifetime:
struct View { data: &'a String } - Use
Rcso both "fields" share ownership without borrowing
- Store an index instead of a reference:
Manual
unsafewithPin:
impl SelfRef {
fn new(data: String) -> Pin> {
let mut boxed = Box::pin(SelfRef { data, ptr: std::ptr::null(), _pin: PhantomPinned });
let ptr = &boxed.data as *const String;
unsafe { boxed.as_mut().get_unchecked_mut().ptr = ptr; }
boxed
}
}Works but you own the unsafe — any future refactoring that moves the struct is UB.
Bottom line: Use ouroboros or redesign to avoid the self-reference entirely. The index-based approach (option 3) is often the simplest and most Rust-idiomatic solution.