溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

怎樣理解Rust中的Pin

發布時間:2021-11-23 16:17:48 來源:億速云 閱讀:170 作者:柒染 欄目:大數據

怎樣理解Rust中的Pin,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

相關概念

Pin<P<T>>
這是一個struct,作用就是將P所指向的T在內存中固定住,不能移動。說白一些,就是不能通過safe代碼拿到&mut T。
Pin<P>  定義如下:
  pub struct Pin<P> {    pointer: P,}
Unpin
這是一個trait,定義在  std::marker  中,如果一個  T: Unpin  ,就說明T在pin后可以安全的移動,實際就是可以拿到&mut T。
  
  
  pub auto trait Unpin {}
!Unpin
對Unpin取反,!Unpin的雙重否定就是pin。如果一個類型中包含了PhantomPinned,那么這個類型就是!Unpin。

pub struct PhantomPinned;
#[stable(feature = "pin", since = "1.33.0")]impl !Unpin for PhantomPinned {}
 

Pin<P>的實現

我們這里只關注safe方法,重點是new方法:
  impl<P: Deref<Target: Unpin>> Pin<P> {    pub fn new(pointer: P) -> Pin<P> {        unsafe { Pin::new_unchecked(pointer) }    }}
可以看出,只有P所指向的  T: Unpin  ,才可以new出一個  Pin<P<T>>  。這里的T就是應該被pin的實例,可是由于  T: Unpin  實際上T的實例并不會被pin。也就是說,T沒有實現Unpin trait時,T才會被真正的pin住。
由于  Pin::new  方法要求  T: Unpin  ,通常創建一個不支持Unpin的T的pin實例的方法是用  Box::pin  方法,定義如下:
  pub fn pin(x: T) -> Pin<Box<T>> {    (box x).into()}
例如,自定義了Node結構,如下的代碼生成pin實例:
  let node_pined: Pin<Box<Node>> = Box::pin(Node::new());let movded_node_pined = node_pined;
Node沒有實現Unpin時,通過Pin的安全方法都不能得到  &mut Node  ,所以就不能移動Node實例。注意,這里是不能移動Node實例,node_pined是Pin實例,是可以移動的。
當然,通過Pin的unsafe方法,仍然可以得到  mut Node  ,也可以移動Node實例,但這些unsafe的操作就需要程序員自己去承擔風險。Pin相關方法中對此有很詳細的說明。
Pin可以被看作一個限制指針(Box<T>&mut T)的結構,在T: Unpin的情況下,Pin<Box<T>>Box<T>是類似的,通過DerefMut就可以直接得到&mut T,在T沒有實現Unpin的情況下,Pin<Box<T>>只能通過Deref得到&T,就是說T被pin住了。
Pin這種自廢武功的方法怪怪的,為什么要有Pin?雖然Box、Rc、Arc等指針類型也可以讓實例在heap中固定,但是這些指針的safe方法會暴露出&mut T,這就會導致T的實例被移動,比如通過  std::mem::swap  方法,也可以是  Option::take  方法,還可能是  Vec::set_len  、  Vec::resize  方法等,這些可都是safe等方法。這些方法的共同點都是需要  &mut Self  ,所以說只要不暴露  &mut Self  ,就可以達到pin的目標。

為什么需要pin?

事情的起因就是Async/.Await異步編程的需要。
看看如下異步編程的代碼:
  let fut_one = /* ... */;let fut_two = /* ... */;async move {    ...    fut_one.await;    ...    fut_two.await;    ...}
rustc在編譯是會自動生成類似如下的代碼,其中的AsyncFuture會是一個自引用結構:
 
// The `Future` type generated by our `async { ... }` blockstruct AsyncFuture {    ...    fut_one: FutOne,    fut_two: FutTwo,    state: State,}
// List of states our `async` block can be inenum State {    AwaitingFutOne,    AwaitingFutTwo,    Done,}
impl Future for AsyncFuture {    type Output = ();
   fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {        ...    }}
 
注意  Future::poll  方法的第一個參數是  Pin<&mut Self>  ,如果在  Future::poll  方法中有類似  std::mem::swap  等方法調用,就有可能導致AsyncFuture被移動,那么AsyncFuture中的自引用field就會導致災難。
可能你也注意到了,這里的  Future::poll  代碼是自動生成的,可以不調用  std::mem::swap  等方法,就不會導致AsyncFuture被移動。的確是這樣的,如果在這里將  Future::poll  的第一個參數改為  Box<Self>  或者  &mut Self  ,大概率是沒有問題的。很多executor的實現,都是要求Future是支持Unpin,因為在poll代碼中的確有修改Self的需求,但不會產生錯誤,也是這個原因。
但是,對于程序員實現Future的情況,問題就來了。**如果poll的參數是&mut Self,那么程序員就可能使用safe代碼(比如std::mem::swap)產生錯誤,這是與rust安全編碼的理念相沖突的。**這就是Pin引入的根本原因!
其實,在future 0.1版本中,poll的這個參數就是  &mut Self  ,如下:
  
  
  pub trait Future {    type Item;    type Error;    fn poll(&mut self) -> Poll<Self::Item, Self::Error>;}
  • Pin實際是對P指針的限制,在T沒有實現Unpin的情況下,避免P指針暴露 &mut Self 。
  • Pin的引入是Async/.Await異步編程的需要,核心就是 Future::poll 方法參數的需要。
  • 除了 Future::poll 方法之外,不建議使用Pin,也沒有必要使用Pin.

關于怎樣理解Rust中的Pin問題的解答就分享到這里了,希望以上內容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關注億速云行業資訊頻道了解更多相關知識。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女