I want to create a self referential struct. I came up with following toy program. Is this correct and safe? If not, could you please explain why and how to fix it? Thanks
use std::pin::Pin;
#[derive(Debug)]
struct A {
s: String,
}
#[derive(Debug)]
struct B<'a> {
w: Vec<&'a str>,
}
impl B<'_> {
fn new(s: &str) -> B {
B {
w: s.split_whitespace().collect(),
}
}
fn leak(self) -> usize {
Box::into_raw(Box::new(self)) as _
}
}
struct C {
a: A,
b: usize,
}
impl C {
fn new(s: String) -> Pin<Box<Self>> {
let a = A { s };
let mut c = Box::new(C { a, b: 0 });
let b = B::new(&c.a.s).leak();
c.b = b;
Pin::new(c)
}
fn a(&self) -> &A {
&self.a
}
fn b(self: Pin<&Self>) -> Option<&B> {
if self.b == 0 {
None
} else {
Some(unsafe { &*(self.b as *const B) })
}
}
fn update(self: Pin<&mut Self>, s: String) {
unsafe { drop_raw(self.b) };
let me = self.get_mut();
me.a.s = s;
me.b = B::new(&me.a.s).leak();
}
}
impl Drop for C {
fn drop(&mut self) {
unsafe { drop_raw(self.b) }
}
}
unsafe fn drop_raw(p: usize) {
if p != 0 {
Box::from_raw(p as *mut B);
}
}
fn main() {
let mut c = C::new(String::from("Hello world"));
println!("{:?}", c.as_ref().a());
println!("{:?}", c.as_ref().b());
c.as_mut().update(String::from("foo bar"));
println!("{:?}", c.as_ref().a());
println!("{:?}", c.as_ref().b());
}
C should be marked Unpin!Unpin otherwise Pin does nothing. The rest looks fine. One thing though, don't use usize for type-erased poinnters, use *mut ()/*const (). This way your code is more clear.