Point to itself

#1

I’m trying to implement a struct with a single field which contains the address of the struct, i.e. something like

struct S {
    myself: *const usize,    // address of the struct itself (is this possible?)
}

a naive implementation is

impl S {
    fn an_instance() -> Self {
        let mut x = Self { myself: ptr::null() as *const usize };
        x.myself = unsafe { mem::transmute(&x) };
        
        // everything seems ok here
        // println!("x: {:p}", &x);
        // println!("x's myself: {:p}", x.myself);

        // but x is moved (how to synchronize x.myself?)
        x 
    }
}

which does not work, e.g. a test:

let xo = S::an_instance();
println!("xo: {:p}", &xo);
println!("xo's myself: {:p}", xo.myself);

which shows:

x: 0x14fc5efb20
x's myself: 0x14fc5efb20
xo: 0x14fc5efc48
xo's myself: 0x14fc5efb20

The reason is straightforward: x is moved while returning from new but x.myself is not synchronized.

How can I keep the invariance between xo and xo.myself? (or forbidding x to be returned from any function!?)

Many thanks for any advice.

#2

You’re looking for Pin, which is unfortunately not yet on stable. There’s an example on that page of how to use this api, but my understanding is that Pin<T> is not allowed to be moved, unless T implements the Unpin trait. Unpin will automatically get implemented for all types with only Unpin members (I think). In this case, since the reason the struct can’t be moved is due to unsafe code, you should add a std::marker::Pinned member to it to prevent it from automatically implementing Unpin. Then change an_instance to return Pin<Self>.

2 Likes
#3
use std::cell::Cell;

#[derive(Debug)]
struct S<'a> {
    myself: Cell<Option<&'a S<'a>>>,
}

impl<'a> S<'a> {
    fn new() -> Self {
        S { myself: Cell::new(None) }
    }
    
    fn pin(&'a self) {
        self.myself.set(Some(self));
    }
}

fn main() {
    let xo = S::new();
    xo.pin();
    println!("xo: {:p}", &xo);
    if let Some(myself) = xo.myself.get() {
        println!("xo's myself: {:p}", myself);
    }

    // Cannot be moved
    // let xo2 = xo;
}
xo: 0x7ffcf9937638
xo's myself: 0x7ffcf9937638

If you uncomment the last line:

error[E0505]: cannot move out of `xo` because it is borrowed
  --> src/main.rs:32:15
   |
25 |     xo.pin();
   |     -- borrow of `xo` occurs here
...
32 |     let xo2 = xo;
   |               ^^
   |               |
   |               move out of `xo` occurs here
   |               borrow later used here
5 Likes
#4

If you’re going to write functions that use S::myself, then perhaps it’d be more logical to write all your functions to get self's address on the fly instead of using a precomputed value, unless you’re going to serialize and deserialize it (Which is really not recommended, y’know, because they’re pointers)

2 Likes
#5

Many thanks for the response and the references. I’m still digesting them but they’re great.

#6

Many thanks for pointing out Pin.

#7

Thanks a lot for the advice.

1 Like