Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does Rust require that "a value, once pinned, must remain pinned forever"?

Tags:

rust

I am confused about the requirement of Pin<P> that

a value, once pinned, must remain pinned forever.

I saw the discussion thread on reddit but after reading through it I remain confused.

To me, it feels more natural if we require the pointee to be pinned when the Pin<P> object is alive, but can re-acquire the ability to move it again after the Pin<P> object is dropped.

As a concrete example,

use std::mem;
use std::pin::Pin;

fn move_pinned_ref<T>(mut a: T, mut b: T) {
    unsafe {
        let p: Pin<&mut T> = Pin::new_unchecked(&mut a);
        // *I prefer* it to mean that the pointee `a` cannot move when `p` is in the scope.
        // But *actually* it means the pointee `a` can never move again.
    }

    mem::swap(&mut a, &mut b);
    // *I prefer* it to be valid because `p` has been dropped.
    // But we *actually* have violated the pinning API contract that a value,
    // once pinned, must remain pinned forever.
}

Can someone please point out why the design I prefer is problematic?

like image 593
Zhiyao Avatar asked Oct 19 '25 18:10

Zhiyao


1 Answers

One important reason for this rule is that it allows self-referential structs to remain valid, even after the Pin is dropped. For example:

use std::marker::PhantomPinned;
use std::pin::Pin;
use std::ptr::NonNull;

struct Unmovable {
    data: String,
    slice: Option<NonNull<String>>,
    _pin: PhantomPinned,
}

impl Unmovable {
    fn update_slice(self: Pin<&mut Self>) {
        unsafe {
            let self_mut = Pin::get_unchecked_mut(self);
            self_mut.slice = Some(NonNull::from(&mut self_mut.data));
        }
    }
    
    fn print_slice(&self) {
        if let Some(s) = self.slice {
            unsafe {
                println!("{}", s.as_ref());
            }
        }
    }
}

fn main() {
    let mut a = Unmovable {
        data: "Hello, world!".into(),
        slice: None,
        _pin: PhantomPinned,
    };

    let p = unsafe { Pin::new_unchecked(&mut a) };
    p.update_slice(); // This causes `p` to be dropped.

    // If we move `a`, even after dropping the Pin:

    let x2 = Box::new(a);

    // Now x2 has a dangling pointer!
    x2.print_slice() // <-- Undefined behavior!
}
like image 105
Coder-256 Avatar answered Oct 22 '25 05:10

Coder-256



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!