千家信息网

怎样理解Rust中的Pin

发表于:2025-01-25 作者:千家信息网编辑
千家信息网最后更新 2025年01月25日,怎样理解Rust中的Pin,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。相关概念Pin>这是一个struct,作用就是将P所指向的T
千家信息网最后更新 2025年01月25日怎样理解Rust中的Pin

怎样理解Rust中的Pin,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。

相关概念

Pin>
这是一个struct,作用就是将P所指向的T在内存中固定住,不能移动。说白一些,就是不能通过safe代码拿到&mut T
Pin

定义如下:

  pub struct Pin

{ 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

的实现

我们这里只关注safe方法,重点是new方法:
  impl> Pin

{ pub fn new(pointer: P) -> Pin

{ unsafe { Pin::new_unchecked(pointer) } }}

可以看出,只有P所指向的 T: Unpin ,才可以new出一个 Pin> 。这里的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 x).into()}
例如,自定义了Node结构,如下的代码生成pin实例:
  let node_pined: Pin> = 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&mut T)的结构,在T: Unpin的情况下,Pin>Box是类似的,通过DerefMut就可以直接得到&mut T,在T没有实现Unpin的情况下,Pin>只能通过Deref得到&T,就是说T被pin住了。
Pin这种自废武功的方法怪怪的,为什么要有Pin?虽然Box、Rc、Arc等指针类型也可以让实例在heap中固定,但是这些指针的safe方法会暴露出&mut T,这就会导致T的实例被移动,比如通过 std::mem::swap 方法,也可以是 Option::take 方法,还可能是 Vec::set_lenVec::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 或者 &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;}
  • Pin实际是对P指针的限制,在T没有实现Unpin的情况下,避免P指针暴露 &mut Self
  • Pin的引入是Async/.Await异步编程的需要,核心就是 Future::poll 方法参数的需要。
  • 除了 Future::poll 方法之外,不建议使用Pin,也没有必要使用Pin.

关于怎样理解Rust中的Pin问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注行业资讯频道了解更多相关知识。

方法 就是 实例 移动 代码 参数 指针 问题 情况 安全 实际 程序 程序员 类型 结构 这是 生成 编程 原因 指向 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 金融网络安全知识大全 天涯明月刀手游捏脸数据库男 网络技术的职位 河南商家云网络技术有限公司 北京首例网络安全审判案 软件开发项目技术水平怎么写 网络安全电脑键盘 云服务器安全设置 北京软件开发人员的薪金 手游原神如何切换服务器 维语软件开发教程 淄博绩效考核hr软件开发公司 更改计算机网络安全标准 软件开发工具项目管理内容 眼镜行业软件开发一般哪家好 广西信息化网络安全工程包括什么 中国统计数据库怎么登录 广州百易网络技术有限公司 阿里云账号登录服务器连接失败 数据库和网络其他系统的通信 路由器与服务器互相通信 华为手机云服务器无法访问 市网络安全应急指挥中心主任 php服务器搭建教程 手机游戏辅助软件开发 全球各国商业信息数据库 软件开发模型 螺旋模型 郑州网络安全法 代理服务器的端口 校园网网络技术的应用
0