Rust 之 Pin和Unpin详解

深入理解Rust异步编程与自引用结构的基石:Pin和Unpin

Posted by Vlor on January 21, 2026

概述

Pin和Unpin是Rust中用于保证对象内存位置稳定性的两个核心标记trait,它们在异步编程、自引用结构和FFI(外部函数接口)等场景中发挥着至关重要的作用。Pin机制通过限制对象的移动能力,在编译期防止因对象移动而导致的不安全问题。

核心概念

什么是Pin和Unpin?

  • Pin: 一种包装器类型,用于固定指针指向的值在内存中的位置,确保该值不会被移动到其他内存地址
  • Unpin: 一个标记trait,表示类型的值可以安全地在内存中移动(大多数类型都自动实现了这个trait)
  • 固定(Fixing): 将数据”钉”在内存中的特定位置,防止其在栈或堆中被移动

为什么需要Pin?

Rust的所有权系统默认允许值在内存中自由移动(如通过move、clone、赋值等操作),这在大多数情况下是安全的且高效的。但在某些特殊场景中,移动对象会导致严重的安全问题:

  1. 自引用结构: 结构体内部包含指向自身字段的指针,移动后指针指向的地址失效
  2. 异步任务: Future对象通常包含自引用状态,移动后会导致内部状态混乱
  3. FFI回调: 外部代码可能持有指向Rust对象的指针,移动会导致悬垂指针

Pin的工作原理

pub struct Pin<P> {
    pointer: P,
}

Pin不改变底层指针的行为,而是通过类型系统约束对被包装对象的操作。当T: !Unpin时,Pin会限制只能通过&mut T访问对象,而不能通过&mut T将其移出。

Unpin Trait详解

定义与作用

Unpin是一个自动trait(auto trait),大多数类型都自动实现它。表示类型的值可以安全地在内存中移动。

pub unsafe auto trait Unpin {}

自动实现规则

Rust编译器会自动为类型实现Unpin,除非:

  1. 类型或其字段手动实现!Unpin
  2. 类型包含!Unpin类型的字段

实现Unpin的类型

// 基本类型都实现了Unpin
let x: i32 = 42; // Unpin ✓
let s: String = "hello".to_string(); // Unpin ✓
let v: Vec<i32> = vec![1, 2, 3]; // Unpin ✓

// 包含Unpin类型的结构体自动实现Unpin
struct MyStruct {
    data: i32,
    name: String,
}
// 自动实现Unpin ✓

非Unpin类型示例

use std::marker::PhantomPinned;

struct NotUnpin {
    _pin: PhantomPinned,
}
// 自动实现!Unpin ✗

// 包含!Unpin类型导致整体!Unpin
struct ContainsNotUnpin {
    data: NotUnpin,
}
// 自动实现!Unpin ✗

Pin Trait详解

定义与作用

Pin是一个包装器类型,用于固定指针指向的值。它有两种变体:

  • Pin<&mut T>: 固定的可变借用
  • Pin<&T>: 固定的不可变借用
impl<P: DerefMut> Pin<P> {
    pub unsafe fn new_unchecked(pointer: P) -> Pin<P> {
        Pin { pointer }
    }

    pub fn into_inner(pin: Pin<P>) -> P {
        pin.pointer
    }
}

impl<P: Deref> Pin<P> {
    pub fn as_ref(&self) -> Pin<&P::Target> {
        // ...
    }

    pub fn as_mut(&mut self) -> Pin<&mut P::Target> where P: DerefMut {
        // ...
    }
}

创建Pin的两种方式

1. 安全方式(仅对Unpin类型)

use std::pin::Pin;

let x = 42;
let mut pinned = Pin::new(&mut x); // ✓ 安全,因为i32: Unpin
*pinned = 100; // 可以修改

// 从Pin中取出值
let x = pinned.into_inner(); // ✓ 安全

2. 不安全方式(对!Unpin类型)

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

struct NotUnpin {
    _pin: PhantomPinned,
    value: i32,
}

impl NotUnpin {
    fn new(value: i32) -> Self {
        Self {
            _pin: PhantomPinned,
            value,
        }
    }
}

let mut x = NotUnpin::new(42);
let pinned = unsafe { Pin::new_unchecked(&mut x) };

// 不能通过Pin取出值
// let x = pinned.into_inner(); // ✗ 编译错误,因为NotUnpin: !Unpin

Pin的使用规则

规则1: 数据固定后不能移动

use std::pin::Pin;

struct SelfRef {
    value: String,
    pointer: *const String, // 自引用指针
}

fn main() {
    let mut data = SelfRef {
        value: String::from("hello"),
        pointer: std::ptr::null(),
    };

    // 设置自引用指针
    data.pointer = &data.value as *const String;

    // 使用Pin固定
    let mut pinned = unsafe { Pin::new_unchecked(&mut data) };

    // 可以安全访问
    let pinned_value: Pin<&mut String> = unsafe {
        pinned.map_unchecked_mut(|s| &mut s.value)
    };

    // 不能移动data
    // let moved = pinned.into_inner(); // ✗ 防止自引用失效
}

规则2: 只能通过Deref访问被固定的值

use std::pin::Pin;

struct NotUnpin {
    value: i32,
}

impl NotUnpin {
    fn get_value(self: Pin<&Self>) -> i32 {
        // 通过Pin访问字段是安全的
        self.value
    }
}

let x = NotUnpin { value: 42 };
let pinned = unsafe { Pin::new_unchecked(&x) };
assert_eq!(pinned.get_value(), 42);

实践应用

自引用结构

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

struct SelfReference {
    data: String,
    pointer: *const String,
    _pin: PhantomPinned,
}

impl SelfReference {
    fn new(data: String) -> Pin<Box<Self>> {
        let mut boxed = Box::pin(SelfReference {
            data,
            pointer: std::ptr::null(),
            _pin: PhantomPinned,
        });

        // 设置自引用指针
        let data_ptr: *const String = &boxed.data;
        unsafe {
            let mut_ref = Pin::as_mut(&mut boxed);
            Pin::get_unchecked_mut(mut_ref).pointer = data_ptr;
        }

        boxed
    }

    fn get_data(self: Pin<&Self>) -> &str {
        &self.data
    }

    fn get_pointer(self: Pin<&Self>) -> *const String {
        self.pointer
    }
}

fn main() {
    let pinned = SelfReference::new(String::from("hello"));

    println!("Data: {}", pinned.get_data());
    println!("Pointer: {:?}", pinned.get_pointer());
}

异步Future中的Pin

use std::pin::Pin;
use std::future::Future;
use std::task::{Context, Poll};

struct DelayedFuture {
    counter: u32,
}

impl Future for DelayedFuture {
    type Output = u32;

    fn poll(mut self: Pin<&mut Self>, _cx: &mut Context) -> Poll<Self::Output> {
        self.counter += 1;
        if self.counter >= 3 {
            Poll::Ready(self.counter)
        } else {
            Poll::Pending
        }
    }
}

// 异步函数展开为Future状态机
async fn delayed() -> u32 {
    let future = DelayedFuture { counter: 0 };
    future.await
}

手动实现Unpin

use std::marker::PhantomPinned;

struct NotUnpin {
    _pin: PhantomPinned,
}

// 手动实现Unpin(不安全,需要谨慎!)
unsafe impl Unpin for NotUnpin {}

// 现在可以安全地移动
let x = NotUnpin { _pin: PhantomPinned };
let pinned = Pin::new(&mut x); // ✓ 现在是安全的

Pin与智能指针

Pin与Box

use std::pin::Pin;

// Box提供了安全的Pin创建
let pinned: Pin<Box<i32>> = Box::pin(42);

// Box::pin适用于!Unpin类型
struct NotUnpin {
    _pin: std::marker::PhantomPinned,
}

let pinned: Pin<Box<NotUnpin>> = Box::pin(NotUnpin {
    _pin: std::marker::PhantomPinned,
});

Pin与Rc/Arc

use std::pin::Pin;
use std::rc::Rc;

// Rc和Arc不能直接创建Pin
// 但可以固定引用
let value = 42;
let rc = Rc::new(value);
let pinned: Pin<&i32> = unsafe { Pin::new_unchecked(&*rc) };

Pin与&/&mut

use std::pin::Pin;

let mut x = 42;
let mut pinned = Pin::new(&mut x); // ✓ 安全

// 对于!Unpin类型
struct NotUnpin {
    _pin: std::marker::PhantomPinned,
}

let mut x = NotUnpin { _pin: std::marker::PhantomPinned };
let pinned = unsafe { Pin::new_unchecked(&mut x) }; // 需要unsafe

常见错误与解决

错误1: 尝试移动!Unpin类型

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

struct NotUnpin {
    _pin: PhantomPinned,
}

let x = Box::pin(NotUnpin { _pin: PhantomPinned });
// let moved = *x; // ✗ 编译错误:Cannot move out of a Pin

解决方案: 使用Pin提供的接口访问数据,而不是尝试移动。

错误2: 创建Pin后修改自引用

use std::pin::Pin;

struct SelfRef {
    data: String,
    pointer: *mut String,
}

// 错误:在固定后修改自引用
// 必须确保自引用指针始终有效

解决方案: 在设置自引用指针后就不再修改,或者提供安全的方法来更新自引用。

错误3: 忽略Pin的安全性保证

use std::pin::Pin;

// 错误:使用unsafe忽略Pin的安全性
struct NotUnpin {
    _pin: std::marker::PhantomPinned,
}

let x = NotUnpin { _pin: std::marker::PhantomPinned };
let mut pinned = unsafe { Pin::new_unchecked(&mut x) };
// let moved = unsafe { Pin::into_inner_unchecked(pinned) }; // 危险!

解决方案: 除非你完全理解后果,否则不要使用Pin::into_inner_unchecked

总结要点

Pin的核心价值

  1. 类型级别的安全保障: 通过类型系统在编译期防止不安全的内存移动
  2. 支持自引用结构: 允许创建包含自引用的数据结构而不破坏安全性
  3. 异步编程基石: 为Future提供必要的稳定性保证
  4. 零成本抽象: Pin在运行时没有任何额外开销

Unpin的重要性

  1. 默认行为: 大多数类型都是Unpin,可以自由移动
  2. 自动实现: 编译器自动实现,无需手动干预
  3. 特殊标记: 只有需要固定时才标记为!Unpin

使用原则

  1. 优先使用Unpin类型: 大多数情况下不需要Pin
  2. 谨慎使用!Unpin: 确保真正需要固定时才使用
  3. 遵循Pin规则: 不要试图绕过Pin的安全性保证
  4. 善用标准库: 使用Box::pin等安全接口

性能考虑

  • Pin在编译期检查,运行时零开销
  • Unpin类型的Pin操作会被完全优化掉
  • !Unpin类型的Pin只在必要时才使用

进阶资源

Rust社区风采

https://example.com/rust-async.jpg

Rust社区致力于构建安全、高效且易于理解的异步编程生态,Pin和Unpin是这一愿景的重要基石