Function f_pin_unpin

Source
pub fn f_pin_unpin()
Expand description

§前奏

Safe Rust 无法构建自引用结构体:

struct SelfReferential<'a> {
    a: String,
    b: &'a String,
}

fn main() {
    let a = String::from("Hello");
    let _sr = SelfReferential { a, b: &a }; // error: borrow of moved value: `a`
}

另一个示例:即便使用 Box<T> 放到堆上,也存在风险:

struct SelfReferential {
    self_ptr: *const Self,
}

fn main() {
    let mut heap_value = Box::new(SelfReferential {
        self_ptr: 0 as *const _,
    });
    let ptr = &*heap_value as *const SelfReferential;
    heap_value.self_ptr = ptr;
    println!("heap value at: {:p}", heap_value);
    println!("internal reference: {:p}", heap_value.self_ptr);

    // 风险代码
    let stack_value = mem::replace(&mut *heap_value, SelfReferential {
        self_ptr: 0 as *const _,
        _pin: PhantomPinned,
    });
    println!("value at: {:p}", &stack_value);
    println!("internal reference: {:p}", stack_value.self_ptr);
}

使用指针来构建,但是有安全风险:

#[derive(Debug)]
struct SelfReferential {
    a: String,
    b: *const String,
}

impl SelfReferential {
    fn new(txt: &str) -> Self {
        Self {
            a: String::from(txt),
            b: std::ptr::null(),
        }
    }

    fn init(&mut self) {
        let self_ref: *const String = &self.a;
        self.b = self_ref;
    }

    fn a(&self) -> &str {
        &self.a
    }

    fn b(&self) -> &String {
        unsafe {&*(self.b)}
    }
}

fn main() {
    let mut sf1 = SelfReferential::new("test1");
    sf1.init();
    let mut sf2 = SelfReferential::new("test2");
    sf2.init();

    println!("a: {}, b: {}", sf1.a(), sf1.b());
    println!("a: {:p}, b: {:p}, t: {:p}", &(sf1.a), sf1.b, &(sf2.a));
    // 使用swap()函数交换两者,这里发生了move
    std::mem::swap(&mut sf1, &mut sf2);


    sf1.a = "I've totally changed now!".to_string();
    println!("a: {}, b: {}", sf2.a(), sf2.b());
    println!("a: {:p}, b: {:p}, t: {:p}", &(sf1.a), sf1.b, &(sf2.a) ) ;
}

Pin 到栈上:

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

#[derive(Debug)]
struct SelfReferential {
    a: String,
    b: *const String,
    _marker: PhantomPinned,
}

impl SelfReferential {
    fn new(txt: &str) -> Self {
        Self {
            a: String::from(txt),
            b: std::ptr::null(),
            _marker: PhantomPinned, // This makes our type `!Unpin`
        }
    }

    fn init<'a>(self: Pin<&'a mut Self>) {
        let self_ptr: *const String = &self.a;
        let this = unsafe { self.get_unchecked_mut() };
        this.b = self_ptr;
    }

    fn a<'a>(self: Pin<&'a Self>) -> &'a str {
        &self.get_ref().a
    }

    fn b<'a>(self: Pin<&'a Self>) -> &'a String {
        unsafe { &*(self.b) }
    }
}

pub fn main() {
    let mut sf1 = unsafe { Pin::new_unchecked(&mut SelfReferential::new("test1")) };
    SelfReferential::init(sf1.as_mut());

    let mut sf2 = unsafe { Pin::new_unchecked(&mut SelfReferential::new("test2")) };
    SelfReferential::init(sf2.as_mut());

    println!("a: {}, b: {}", SelfReferential::a(sf1.as_ref()), SelfReferential::b(sf1.as_ref()));
    std::mem::swap(sf1.get_mut(), sf2.get_mut());
    println!("a: {}, b: {}", SelfReferential::a(sf2.as_ref()), SelfReferential::b(sf2.as_ref()));
}

一个 Pin<&mut T> 必须在被引用的T的整个生命周期被保持 pinned,这对于栈上的变量很难确认。 为了帮助处理这类问题,就有了像pin-utils这样的 crate。

栈上 vs 堆上:

fn main(){

    let s = "hello".to_string();
    let p = s.as_str();
    println!("{:p}", p);
    println!("{:p}", s.as_ptr());
    println!("{:p}", &s);
}

Pin 到 堆上:

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

#[derive(Debug)]
struct SelfReferential {
    a: String,
    b: *const String,
    _marker: PhantomPinned,
}

impl SelfReferential {
    fn new(txt: &str) -> Pin<Box<Self>> {
        let t = Self {
            a: String::from(txt),
            b: std::ptr::null(),
            _marker: PhantomPinned,
        };
        let mut boxed = Box::pin(t);
        let self_ptr: *const String = &boxed.as_ref().a;
        unsafe { boxed.as_mut().get_unchecked_mut().b = self_ptr };

        boxed
    }

    fn a<'a>(self: Pin<&'a Self>) -> &'a str {
        &self.get_ref().a
    }

    fn b<'a>(self: Pin<&'a Self>) -> &'a String {
        unsafe { &*(self.b) }
    }
}

pub fn main() {
    let mut sf1 = SelfReferential::new("test1");
    let mut sf2 = SelfReferential::new("test2");

    println!("a: {}, b: {}",sf1.as_ref().a(), sf1.as_ref().b());
    std::mem::swap(sf1.get_mut(), sf1.get_mut());
    // std::mem::swap(&mut *sf1, &mut *sf2);
    println!("a: {}, b: {}",sf2.as_ref().a(), sf2.as_ref().b());
}

另外一个示例:

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

struct SelfReferential {
    self_ptr: *const Self,
    _pin: PhantomPinned,
}

fn main() {
    let mut heap_value = Box::pin(SelfReferential {
        self_ptr: 0 as *const _,
        _pin: PhantomPinned,
    });
    let ptr = &*heap_value as *const SelfReferential;

    // 这是安全的,因为修改结构体字段不会让结构体发生move
    unsafe {
        let mut_ref = Pin::as_mut(&mut heap_value);
        Pin::get_unchecked_mut(mut_ref).self_ptr = ptr;
    }

    println!("heap value at: {:p}", heap_value);
    println!("internal reference: {:p}", heap_value.self_ptr);

    // 有效阻止了下面风险代码的发生

    let stack_value = mem::replace(&mut *heap_value, SelfReferential {
        self_ptr: 0 as *const _,
        _pin: PhantomPinned,
    });
    println!("value at: {:p}", &stack_value);
    println!("internal reference: {:p}", stack_value.self_ptr);
}



§异步实现细节:Pin 与 UnPin

async/await 的要支持引用,就必须支持自引用结构。

Rust 源码内部:https://github.com/rust-lang/rust/blob/master/library/core/src/future/mod.rs

// From Generator to Future
pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
where
    T: Generator<ResumeTy, Yield = ()>,
{
    #[rustc_diagnostic_item = "gen_future"]
    struct GenFuture<T: Generator<ResumeTy, Yield = ()>>(T);

    // 依赖这样一个重要事实:
    // 即 async/await Future 是不可移动的,以便在基础生成器中创建自引用借用。
    impl<T: Generator<ResumeTy, Yield = ()>> !Unpin for GenFuture<T> {}

    impl<T: Generator<ResumeTy, Yield = ()>> Future for GenFuture<T> {
        type Output = T::Return;
        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
            // SAFETY: Safe because we're !Unpin + !Drop, and this is just a field projection.
            let gen = unsafe { Pin::map_unchecked_mut(self, |s| &mut s.0) };

            // Resume the generator, turning the `&mut Context` into a `NonNull` raw pointer. The
            // `.await` lowering will safely cast that back to a `&mut Context`.
            // resume 里传递上下文指针
            match gen.resume(ResumeTy(NonNull::from(cx).cast::<Context<'static>>())) {
                GeneratorState::Yielded(()) => Poll::Pending,
                GeneratorState::Complete(x) => Poll::Ready(x),
            }
        }
    }

    GenFuture(gen)
}

Pin 利用类型系统避免 &mut T 被拿到,达到 固定 引用/指针 的目的。

https://doc.rust-lang.org/std/pin/index.html

§Pin 与 Unpin 属性

来自:Asynchronous Programming in Rust

  • 如果 T: Unpin(默认会实现),那么 Pin<'a, T> 完全等价于 &'a mut T。换言之: Unpin 意味着这个类型被移走也没关系,就算已经被固定了,所以 Pin 对这样的类型毫无影响。

  • 如果 T: !Unpin, 获取已经被固定的 T 类型示例的 &mut T需要 unsafe

  • 标准库中的大部分类型实现 Unpin,在 Rust 中遇到的多数普通类型也是一样。但是, async/await 生成的 Future 是个例外。

  • 你可以在 nightly 通过特性标记来给类型添加 !Unpin 约束,或者在 stable 给你的类型加 std::marker::PhatomPinned 字段。

  • 你可以将数据固定到栈上或堆上

  • 固定 !Unpin 对象到栈上需要 unsafe

  • 固定 !Unpin 对象到堆上不需要 unsafeBox::pin可以快速完成这种固定。

  • 对于 T: !Unpin 的被固定数据,你必须维护好数据内存不会无效的约定,或者叫 固定时起直到释放。这是 固定约定 中的重要部分。

§Pin 用法约定

  • 带 Pin 结构化包装的 投影 (structural Pin projection) : Pin<&mut Wrapper<Field>> -> Pin<&mut Field>pin_utils::unsafe_pinned! 宏可以做。
    • 当结构体所有的字段都实现 Unpin ,整个结构体才可以实现 Unpin。(不允许任何实现使用不安全的方式将此类字段移出,比如 Option::take 是被禁止的)
    • 结构体不能用 #[repr(packed)]
    • 如果Drop::drop不移动任何字段,则整个结构只能实现Drop
  • 不带 Pin 结构化包装的 投影 (no structural Pin projection) : Pin<&mut Wrapper<Field>> -> &mut Fieldpin_utils::unsafe_unpinned! 宏可以做的。

参考: futures-util::future::map

impl<Fut, F> Map<Fut, F> {
    unsafe_pinned!(future: Fut); // pin projection -----+
    unsafe_unpinned!(f: Option<F>); // not pinned --+   |
//                                                  |   |
//                 ...                              |   |
//                                                  |   |
    fn poll (mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<T> {
        //                                          |   |
        match self.as_mut().future().poll(cx) { // <----+ required here
            Poll::Pending => Poll::Pending, //      |
            Poll::Ready(output) => { //             |
                let f = self.f().take() // <--------+ allows this