为什么需要 Pin 假如我们有一个对象定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 use std::pin::Pin;#[derive(Debug)] struct Test { a: String , b: *const String , } impl Test { fn new (txt: &str ) -> Self { Test { 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 { assert! (!self .b.is_null (), "Test::b called without Test::init being called first" ); unsafe { &*(self .b) } } }
常规情况下
1 2 3 4 5 6 7 8 9 10 fn main () { let mut test1 = Test::new ("test1" ); test1.init (); let mut test2 = Test::new ("test2" ); test2.init (); println! ("a: {}, b: {}" , test1.a (), test1.b ()); println! ("a: {}, b: {}" , test2.a (), test2.b ()); }
输出是
1 2 a: test1, b: test1 a: test2, b: test2
我们加上一个 swap
呢?
1 2 3 4 5 6 7 8 9 10 11 fn main () { let mut test1 = Test::new ("test1" ); test1.init (); let mut test2 = Test::new ("test2" ); test2.init (); println! ("a: {}, b: {}" , test1.a (), test1.b ()); std::mem::swap (&mut test1, &mut test2); println! ("a: {}, b: {}" , test2.a (), test2.b ()); }
结果答案就变成了
1 2 a: test1, b: test1 a: test1, b: test2
原因就是因为我们将对象的指针交换了,但是并没有保证内部引用 b 还是指向当前的自己。
解决之道 显然我们需要保证 b
所指向的 Self 并不能被修改,因此 Pin
可以这么使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 use std::pin::Pin;use std::marker::PhantomPinned;#[derive(Debug)] struct Test { a: String , b: *const String , _marker: PhantomPinned, } impl Test { fn new (txt: &str ) -> Self { Test { a: String ::from (txt), b: std::ptr::null (), _marker: PhantomPinned, } } 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 { assert! (!self .b.is_null (), "Test::b called without Test::init being called first" ); unsafe { &*(self .b) } } }
我们此时使用之前的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 pub fn main () { let mut test1 = Test::new ("test1" ); let mut test1 = unsafe { Pin::new_unchecked (&mut test1) }; Test::init (test1.as_mut ()); let mut test2 = Test::new ("test2" ); let mut test2 = unsafe { Pin::new_unchecked (&mut test2) }; Test::init (test2.as_mut ()); println! ("a: {}, b: {}" , Test::a (test1.as_ref ()), Test::b (test1.as_ref ())); std::mem::swap (test1.get_mut (), test2.get_mut ()); println! ("a: {}, b: {}" , Test::a (test2.as_ref ()), Test::b (test2.as_ref ())); }
会输出如下错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 /Users/yanick/.cargo/bin/cargo build --color=always --message-format=json-diagnostic-rendered-ansi --package helloworld --bin helloworld Compiling helloworld v0.1.0 (/Users/yanick/Codes/rust/helloworld) error[E0277]: `PhantomPinned` cannot be unpinned --> src/main.rs:46:26 | 46 | std::mem::swap(test1.get_mut(), test2.get_mut()); | ^^^^^^^ within `Test`, the trait `Unpin` is not implemented for `PhantomPinned` | = note: required because it appears within the type `Test` error[E0277]: `PhantomPinned` cannot be unpinned --> src/main.rs:46:43 | 46 | std::mem::swap(test1.get_mut(), test2.get_mut()); | ^^^^^^^ within `Test`, the trait `Unpin` is not implemented for `PhantomPinned` | = note: required because it appears within the type `Test` error: aborting due to 2 previous errors For more information about this error, try `rustc --explain E0277`. error: could not compile `helloworld` To learn more, run the command again with --verbose. Process finished with exit code 101
从编译时期保证我们的指针不能够被移动。
Pin in Asynchronous 加入我们有个 两个 funture
任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 struct AsyncFuture { fut_one: FutOne, fut_two: FutTwo, state: State, } enum State { AwaitingFutOne, AwaitingFutTwo, Done, } impl Future for AsyncFuture { type Output = (); fn poll (mut self : Pin<&mut Self >, cx: &mut Context<'_ >) -> Poll<()> { loop { match self .state { State::AwaitingFutOne => match self .fut_one.poll (..) { Poll::Ready (()) => self .state = State::AwaitingFutTwo, Poll::Pending => return Poll::Pending, } State::AwaitingFutTwo => match self .fut_two.poll (..) { Poll::Ready (()) => self .state = State::Done, Poll::Pending => return Poll::Pending, } State::Done => return Poll::Ready (()), } } } }
如果在执行 fut_one.poll
时候并没有就绪,显然我们会继续执行 fut_two
如果 async
中包含的执行体是这样的
1 2 3 4 5 6 async { let mut x = [0 ; 128 ]; let read_into_buf_fut = read_into_buf (&mut x); read_into_buf_fut.await ; println! ("{:?}" , x); }
在编译的时候,会进行转为为如下格式
1 2 3 4 5 6 7 8 struct ReadIntoBuf <'a > { buf: &'a mut [u8 ], } struct AsyncFuture { x: [u8 ; 128 ], read_into_buf_fut: ReadIntoBuf<'what_lifetime ?>, }
显然 ReadIntoBuf
持有了 AsyncFunture
中的一个变量,如果 Move
AsyncFuture
就会导致 x
失效。因此我们需要保证 AsyncFuture
不会被 Move
(值得注意的这是一个编译期的保证)
在 poll
函数定义中,我们可以发现定义如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #[stable(feature = "futures_api" , since = "1.36.0" )] impl <F: ?Sized + Future + Unpin> Future for &mut F { type Output = F::Output; fn poll (mut self : Pin<&mut Self >, cx: &mut Context<'_ >) -> Poll<Self ::Output> { F::poll (Pin::new (&mut **self ), cx) } } #[stable(feature = "futures_api" , since = "1.36.0" )] impl <P> Future for Pin <P>where P: Unpin + ops::DerefMut<Target: Future>, { type Output = <<P as ops::Deref>::Target as Future>::Output; fn poll (self : Pin<&mut Self >, cx: &mut Context<'_ >) -> Poll<Self ::Output> { Pin::get_mut (self ).as_mut ().poll (cx) } }
如果不是一个 Unpin
的对象,我们就创建一个 Pin
来 Poll
,最终都需要接受一个被 Pin
的任务来执行。
参考文档