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
对象到堆上不需要unsafe
。Box::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
- 当结构体所有的字段都实现 Unpin ,整个结构体才可以实现 Unpin。(不允许任何实现使用不安全的方式将此类字段移出,比如
- 不带 Pin 结构化包装的 投影 (
no structural Pin projection
) :Pin<&mut Wrapper<Field>> -> &mut Field
。pin_utils::unsafe_unpinned!
宏可以做的。
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