Rust: Pin 和Unpin之啰里八嗦

继续学习Future,今天就不得不研究Pin,我以为挺简单的,然而...

简单的说(总结)

  • Pin:是钉住,压住的意思。放在rust中,其中Pin是一个结构体,全类型是Pin:固定住Ptr的意思。
  • Unpin是一个trait。Rust中所有的类型默认都是Unpin的。

而Pin结构体是!Unpin的,也就是说不能移动的。Rust中还有个标记类型PhantomPinned。这个只是个标记。如果一个结构体中有这个,则表示这个结构体不能被移动。这里的不能被移动是指在被Pin住后,不能被移动。(PS:Rust中的移动有好几层意思)

Pin一般的使用场景是在Future中,因为Future实际上编译后就是状态机,而Future又会引用一些变量,这些变量在Future执行时为了保证内存地址不变,所以必须pin起来,而一般不是pin某个单独的变量,是将整体pin起来。

一个在堆上自引用的例子

struct HeapSelfRef {
    v: Vec<u8>,
    ptr: *const u8, //v的地址
}
impl HeapSelfRef {
    fn new(v: Vec<u8>) -> Self {
        let ptr = v.as_ptr();
        Self { v, ptr }
    }
    fn display(&self) {
        println!("the address of &v {:p}", &self.v);
        println!("the address of v current {:?}", self.v.as_ptr());
        println!("the address of ptr {:?}", self.ptr);
    }
}

fn test_heap() {
    let heap = HeapSelfRef::new(vec![1, 2, 3]);
    heap.display();
    //移动heap
    let heap2 = heap;
    //通过打印,发现确实移动了,&v的地址(栈上)不同,但v堆上的地址没变
    heap2.display();
}

上面的例子因为数据放在堆上,所以栈上的地址(堆的引用)变了,但堆上本身的地址没变。堆上的地址永远不会变?感觉也不一定

再来一个栈上自引用的例子

struct StackSelfRef {
    v: u8,
    ptr: *const u8, //v的地址
}

impl StackSelfRef {
    fn new(v: u8) -> Self {
        let mut s = Self {
            v,
            ptr: std::ptr::null(),
        };
        s.ptr = std::ptr::addr_of!(s.v);
        println!("{}", unsafe { *s.ptr });
        s
    }
    fn display(&self) {
        //此时的ptr指向的已经地址已经不是v的地址了,因为在let s=StackSelfRef::new(),返回s的时候,已经发生了移动。
        //建议是1.不引用栈上的地址,使用Box将数值存放到堆上。2.使用pin,将结构体在内存中的地址固定住。如下一个例子
        println!("{}", unsafe { *self.ptr });
        println!("the address of &v {:p}", &self.v);
        println!(
            "the address of v at current {:?}",
            std::ptr::addr_of!(self.v)
        );
        println!("the address of ptr {:?}", self.ptr);
    }
}
fn test_stak() {
    let s = StackSelfRef::new(8);
    s.display();
}

上面的例子中,在new之后,ptr就已经成了悬垂指针(空引用)了。

实现了Pin的例子

///使用pin将结构体在内存中的位置固定住不移动
///
struct StackPinedSelfRef {
    v: u8,
    ptr: *const u8,
    // _pinned:PhantomPinned,
}

impl StackPinedSelfRef {
    fn new(x: u8) -> Pin<&'static mut StackPinedSelfRef> {
        let s = Self {
            v: x,
            ptr: std::ptr::null(),
            // _pinned:PhantomPinned,
        };
        let s = Box::new(s);
        let s = Box::leak(s);
        let mut pined = Pin::static_mut(s);
        pined.as_mut().ptr = std::ptr::addr_of!(pined.as_ref().v);
        pined
    }
    fn display(&self) {
        println!("{}", unsafe { *self.ptr });
        println!("the address of &v {:p}", &self.v);
        println!(
            "the address of v at current {:?}",
            std::ptr::addr_of!(self.v)
        );
        println!("the address of ptr {:?}", self.ptr);
    }
}

fn test_stackpinned() {
    let s = StackPinedSelfRef::new(10);
    s.display();
    let s2 = s;
    s2.display();
}

写法纯属自创,根据自己对pin的理解一点点拼凑出来的,可能有不严谨的地方。
这里边有一个关键的地方:Pin接收的是指针类型的参数(要求Ptr实现Deref trait),而Pin保证的不是Ptr指针的固定,是Ptr指针指向值的内存位置固定。

总结

Pin<T:Unpin> 表示T类型可以安全移动,Pin对这种类型的不起作用。
Pin<T:!Unpin> 表示T类型不能安全移动,Pin只对这种类型起限制作用。

posted @ 2025-11-14 10:11  薄醉愁听花  阅读(0)  评论(0)    收藏  举报