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

1

10

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?

Queensland answered 16/4, 2018 at 14:46 Comment(0)
M
9

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.

Mackenie answered 16/4, 2018 at 15:18 Comment(3)
@udoprog: I have reworded the section in question. I did not wish to "bash" the OP, just draw attention to the fact that the use of unsafe (and raw pointers) was likely deliberate, and a clue as to the issue.Mackenie
@MatthieuM: Thanks for your answer. Now I understand the problem with my code. I was hoping Pin could solve this issue with self referential structures... It seems that rental and owning-ref are the only solutions for now.Urbina
@SébastienChapuis it does "solve" the overall issue, in a fashion. The problem is that values are not guaranteed to remain at a stable address, and references require that they do. See Why can't I store a value and a reference to that value in the same struct? for a larger discussion and details. I expect that rental and owning-ref will be updated to use Pin / PinBox instead of their own custom "stable address" traits.Resurge

© 2022 - 2024 — McMap. All rights reserved.