溫馨提示×

溫馨提示×

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

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

怎樣理解Rust中的Pin

發(fā)布時(shí)間:2021-11-23 16:17:48 來源:億速云 閱讀:149 作者:柒染 欄目:大數(shù)據(jù)

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

相關(guān)概念

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

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

Pin<P>的實(shí)現(xiàn)

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

為什么需要pin?

事情的起因就是Async/.Await異步編程的需要。
看看如下異步編程的代碼:
  let fut_one = /* ... */;let fut_two = /* ... */;async move {    ...    fut_one.await;    ...    fut_two.await;    ...}
rustc在編譯是會(huì)自動(dòng)生成類似如下的代碼,其中的AsyncFuture會(huì)是一個(gè)自引用結(jié)構(gòu):
 
// 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  方法的第一個(gè)參數(shù)是  Pin<&mut Self>  ,如果在  Future::poll  方法中有類似  std::mem::swap  等方法調(diào)用,就有可能導(dǎo)致AsyncFuture被移動(dòng),那么AsyncFuture中的自引用field就會(huì)導(dǎo)致災(zāi)難。
可能你也注意到了,這里的  Future::poll  代碼是自動(dòng)生成的,可以不調(diào)用  std::mem::swap  等方法,就不會(huì)導(dǎo)致AsyncFuture被移動(dòng)。的確是這樣的,如果在這里將  Future::poll  的第一個(gè)參數(shù)改為  Box<Self>  或者  &mut Self  ,大概率是沒有問題的。很多executor的實(shí)現(xiàn),都是要求Future是支持Unpin,因?yàn)樵趐oll代碼中的確有修改Self的需求,但不會(huì)產(chǎn)生錯(cuò)誤,也是這個(gè)原因。
但是,對于程序員實(shí)現(xiàn)Future的情況,問題就來了。**如果poll的參數(shù)是&mut Self,那么程序員就可能使用safe代碼(比如std::mem::swap)產(chǎn)生錯(cuò)誤,這是與rust安全編碼的理念相沖突的。**這就是Pin引入的根本原因!
其實(shí),在future 0.1版本中,poll的這個(gè)參數(shù)就是  &mut Self  ,如下:
  
  
  pub trait Future {    type Item;    type Error;    fn poll(&mut self) -> Poll<Self::Item, Self::Error>;}
  • Pin實(shí)際是對P指針的限制,在T沒有實(shí)現(xiàn)Unpin的情況下,避免P指針暴露 &mut Self 。
  • Pin的引入是Async/.Await異步編程的需要,核心就是 Future::poll 方法參數(shù)的需要。
  • 除了 Future::poll 方法之外,不建議使用Pin,也沒有必要使用Pin.

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

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI