Rust中的Pin详解
原创 automanyang Rust语言中文社区 昨天
https://mp.weixin.qq.com/s/PjctbPbyR5OeaqTHZdB5uQ
相关概念
Pin<P>
这是一个struct,作用就是将P所指向的T在内存中固定住,不能移动。说白一些,就是不能通过safe代码拿到&mut T。
Pin

定义如下:

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

的实现
我们这里只关注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就是应该被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或&mut T)的结构,在T: Unpin的情况下,Pin<Box>和Box是类似的,通过DerefMut就可以直接得到&mut T,在T没有实现Unpin的情况下,Pin<Box>只能通过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 { ... }` block
struct AsyncFuture {...fut_one: FutOne,fut_two: FutTwo,state: State,
}// List of states our `async` block can be in
enum 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<Self::Item, Self::Error>;
}

总结一下

Pin实际是对P指针的限制,在T没有实现Unpin的情况下,避免P指针暴露&mut Self。
Pin的引入是Async/.Await异步编程的需要,核心就是Future::poll方法参数的需要。
除了Future::poll方法之外,不建议使用Pin,也没有必要使用Pin.

转: Rust中的Pin详解 【Rust语言中文社区】相关推荐

  1. rust的矿坑_转: Rust中的Pin详解 【Rust语言中文社区】

    Rust中的Pin详解 原创 automanyang Rust语言中文社区 昨天 https://mp.weixin.qq.com/s/PjctbPbyR5OeaqTHZdB5uQ 相关概念 Pin ...

  2. c4.5算法 程序语言,决策树之C4.5算法详解-Go语言中文社区

    决策树之C4.5算法详解 主要内容 C4.5算法简介 分裂属性的选择--信息增益率 连续型属性的离散化处理 剪枝--PEP(Pessimistic Error Pruning)剪枝法 缺失属性值的处理 ...

  3. Rust中的Pin详解

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

  4. linux权限管理详解,linux权限管理详解-Go语言中文社区

    普通权限 -rw-r–r--. 1 root root 0 Feb 14 23:33 test 第1位标识文件类型: -:普通文件 b:块设备 c:字符设备 d:目录 l:符号链接 s:套接字文件 p ...

  5. 2017届蓝桥杯java_2017第八届蓝桥杯JavaC组决赛(国赛)试题汇总及试题详解-Go语言中文社区...

    目录 题解待更新 第一题:数位和 题目描述 数学家高斯很小的时候就天分过人.一次老师指定的算数题目是:1+2+...+100. 高斯立即做出答案:5050! 这次你的任务是类似的.但并非是把一个个的数 ...

  6. java+classpath的理解_Java配置path和classpath的含义详解-Go语言中文社区

    ​ 新入门java的同学经常会问我path和classpath是什么意思,尤其会问classpath是什么意思,为什么只配置一个.就可以.下面以windows系统为例,做一个系统讲解.包含超详细实验数 ...

  7. linux sar使用方法,Linux系列之SAR命令使用详解-Go语言中文社区

    1. CPU利用率 sar -p (查看全天) sar -u 1 10 (1:每隔一秒,10:写入10次) 1.1. CPU输出项说明 输出项 详细说明 CPU all 表示统计信息为所有 CPU 的 ...

  8. linux xmanager端口,Xmanager远程连接Linux系统图形界面详解-Go语言中文社区

    1.首先安装与下载图形界面GNOME yum groupinstall "Desktop" yum groupinstall "X Window System" ...

  9. rust 枪法_RUST枪法详解 RUST枪法受哪些因素影响

    一,枪法是什么? 枪法就是从你看见敌人到杀死敌人的时间.枪法好的人,这个时间将非常短,甚至你都来不及反应就被爆头了.而枪法差的人,这个时间将非常长,我甚至见过跟在敌人后面30发子弹打完都打不死的朋友. ...

  10. c语言程序设计中北答案详解,C语言程序设计试题及答案解析汇编.doc

    C语言程序设计试题及答案解析汇编 C语言程序设计试题 第1.2.3章 概述.类型.表达式 一.选择题 一个C程序由若干个C函数组成,各个函数在文件中的位置顺序为:( ) 任意 第一个函数必须是主函数, ...

最新文章

  1. AI一分钟 | 张亚勤:保护用户数据安全是百度的第一原则;美国开通全球首条高速自动驾驶测试路...
  2. UE4 使用VaRest的最佳实践
  3. 怎样在C语言程序中使用功能键和箭头键?
  4. Badger DAO公布系统乘数奖励机制,新增时间加权奖励
  5. idea中npm安装总结与node-sass依赖安装等常见问题避坑总结
  6. 用故事来给你讲负载均衡的原理
  7. 前端零基础入门: 用css设置文字样式
  8. QString中去除空格
  9. R 语言数据处理入门-2(缺失值处理)
  10. 计算机经常断开网络,当笔记本电脑经常自动断开网络连接时如何解决问题
  11. 高版本IE中弹出窗口不显示IE地址栏的解决方法
  12. 幼儿园体育游戏电子计算机教案,【小班体育活动】_小班体育教案100篇_小班体育游戏活动教案5篇...
  13. JavaScript实现贪吃蛇小游戏
  14. 【计算机导论调研报告】计算机从业人员的职业道德
  15. 22.JS+jQuery
  16. eggjs框架学习心得
  17. 微软求职攻略之笔试答疑
  18. catia投图只投外轮廓线_CATIA出工程图时,它只显示所有的轮廓线条,不显示里面的其它线条,这是怎么回事啊?求教高手!!!...
  19. 调试平衡小车过程中间遇到的问题
  20. lombok构造模式

热门文章

  1. python之类之多继承
  2. LoadRunner场景参数文件部分参数说明
  3. 如何编译符合自己路由器的的OpenWrt固件
  4. Linux 2.6下Driver开发的34个变化
  5. MySQL-第八篇MySQL内置函数
  6. 联合主键用hibernate注解映射方式主要有三种:
  7. ubuntu硬盘安装及启动,menu.lst
  8. devc++编程 error: jump to case label错误笔记
  9. 毕业设计,MD2模型动画展示
  10. C# 2.0泛型编程基础(1)