Search code examples
rustrust-pin

How to use the Pin struct with self-referential structures?


I'm trying to use the new Pin feature. After reading this blog post, I've started to write some code:

#![feature(pin, arbitrary_self_types)]
use std::mem::Pin;

pub struct Foo {
    var: i32,
}

pub struct FooRef<'i> {
    ref_var: &'i i32,
    other: i32,
}

pub struct SimpleRef<'data> {
    foo: Box<Foo>,
    bar: Option<FooRef<'data>>,
}

impl<'data> SimpleRef<'data> {
    fn new() -> SimpleRef<'data> {
        SimpleRef {
            foo: Box::new({ Foo { var: 42 } }),
            bar: None,
        }
    }

    fn init(mut self: Pin<SimpleRef<'data>>) {
        let this: &mut SimpleRef = unsafe { Pin::get_mut(&mut self) };
        let a = FooRef {
            ref_var: &this.foo.var,
            other: 12,
        };
        this.bar = Some(a);
    }
}

fn main() {}

But I get this error:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
  --> src/main.rs:27:45
   |
27 |         let this: &mut SimpleRef = unsafe { Pin::get_mut(&mut self) };
   |                                             ^^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 26:5...
  --> src/main.rs:26:5
   |
26 | /     fn init(mut self: Pin<SimpleRef<'data>>) {
27 | |         let this: &mut SimpleRef = unsafe { Pin::get_mut(&mut self) };
28 | |         let a = FooRef {
29 | |             ref_var: &this.foo.var,
...  |
32 | |         this.bar = Some(a);
33 | |     }
   | |_____^
note: ...but the lifetime must also be valid for the lifetime 'data as defined on the impl at 18:1...
  --> src/main.rs:18:1
   |
18 | impl<'data> SimpleRef<'data> {
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: ...so that the expression is assignable:
           expected &mut std::mem::Pin<'_, SimpleRef<'_>>
              found &mut std::mem::Pin<'_, SimpleRef<'data>>

The difference between my code and the code in the blog post is that I'm using a variable with a lifetime parameter, instead of a raw pointer.
Is it possible to use variables with lifetime parameters with Pin?


Solution

  • Is it possible to use variables with lifetime parameters with Pin?

    Probably.

    The difference between my code and the code in the blog post is that I'm using a variable with a lifetime parameter, instead of a raw pointer.

    This changes everything: the Rust language makes no guarantee on the validity of pointers, but makes strict guarantees on the validity of references.

    Let's examine the lifetime issue of references and see why the article specifically used raw pointers (and unsafe code) to overcome the issue.


    The signature of Pin::get_mut is:

    pub unsafe fn get_mut<'b>(this: &'b mut Pin<'a, T>) -> &'b mut T
    

    That is, the reference is only valid as long as the reference to Pin is valid.

    Since Pin is passed by value as argument, it is dropped at the end of the function scope. Yet, you attempt to retain a reference to it beyond that point. This is unsafe.

    Using raw pointers is fine (unchecked), because whoever attempts to use the raw pointer will need to use an unsafe block, taking responsibility for ensuring that the pointer is indeed valid.