Rust中的Pin详解

原创 automanyang Rust语言中文社区 昨天

https://mp.weixin.qq.com/s/PjctbPbyR5OeaqTHZdB5uQ

相关概念

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_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, cx: &mut Context) -> Poll {

...

}

}

注意Future::poll方法的第一个参数是Pin,如果在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<:item self::error>;

}

总结一下

Pin实际是对P指针的限制,在T没有实现Unpin的情况下,避免P指针暴露&mut Self。

Pin的引入是Async/.Await异步编程的需要,核心就是Future::poll方法参数的需要。

除了Future::poll方法之外,不建议使用Pin,也没有必要使用Pin.

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

  1. Rust中的Pin详解

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

  2. java中的map是什么_转载java中Map的详解

    Map简介 什么是map? map是一个接口  是一个将建key 映射到值的对象. map的主要作用是什么? 可以通过创建一个map的实现类 来存放 数据 值 和值的描述 也可以通过描述去取得数据 将 ...

  3. python字典统计字母出现次数_第三篇 python运用字典统计字符串中字母出现的次数-Go语言中文社区...

    碎碎念 这个内容还是我日常互相种草的好友提供的素材,很基础也很实用,稍微进阶一些就可以用来统计文章中的单词出现的频率了.她在网上找的代码用了库,通过python中的字典可以很简洁的完成.(下图是她在网 ...

  4. vue打印props的值_关于Vue中props的详解

    看一下官方文档: 组件实例的作用域是孤立的.这意味着不能 (也不应该) 在子组件的模板内直接引用父组件的数据.父组件的数据需要通过 prop 才能下发到子组件中. 也就是props是子组件访问父组件数 ...

  5. 给 python 初学者的四条忠告_给 python 初学者的四条忠告-Go语言中文社区

    1. 不要纠结于开发工具的选择,简单直接就是最好的 学习一种编程语言,首先要找一款合用的集成开发工具,似乎是自然而然的想法.为什么不呢?IDE可以自动补齐,可以一键运行,还可以断点调试.使用IDE开发 ...

  6. python爬虫过程中遇到的问题_python爬虫过程中出现的问题汇总-Go语言中文社区

    1.出现 UnicodeDecodeError: 'ascii' codec can't decode byte 0xe5 in position 7: ordinal not in range(12 ...

  7. python基本图形绘制_【Python】Python基本图形绘制-Go语言中文社区

    1.Python蟒蛇图形绘制: 代码: #PythonDraw.py import turtle turtle.setup(650, 350, 200, 200) turtle.penup() tur ...

  8. mysql索引 钱缀_【mysql索引】之前缀索引-Go语言中文社区

    第零步:简单说一说 有时候需要索引很长的字符(例如BLOB,TEXT,或者很长的VARCHAR),这样会使得索引又大又慢. 改良方法有:1.改用哈希索引(这里不讲).2.使用字符串的前几个字符作为索引 ...

  9. python语言例子_【Python】SimPy的使用示例-Go语言中文社区

    使用SimPY进行离散事件仿真 SimPY是一个Python下的第三方库,可以方便的进行离散事件的仿真.仿真速度比较快.下面记录一下我的一点心得,不保证完全正确,供参考. 安装 $ pip insta ...

最新文章

  1. 机器学习误差分析(Error Analysis)实战
  2. python 解码json数据并在一个OrderdDict中保留其顺序
  3. 在项目里交叉使用Swift和OC
  4. 解决链接模型的可见性问题
  5. (转)在Windows上安装GPU版Tensorflow
  6. MyEclipse-6.5注冊码生成器源代码
  7. .NET程序在运行中创建EXE文件的技术
  8. Silverlight+WCF+ArcObjects获取地图服务图层列表的实现
  9. NeurIPS 2021揭榜,接收率创九年新高,论文列表已公布,你的文章中了吗?
  10. 测试有道:微软测试技术心得
  11. 背包问题 尽可能大和大于某个值 为什么是一样意思
  12. 宋宝华: CPU是如何访问到内存的?--MMU最基本原理
  13. paip.编程语言到底有没有优劣之分优秀之分
  14. 金蝶14.0系统服务器安装教程,大神面对win7系统安装金蝶kis14.0的方式
  15. ip ,子网掩码, 网关 ,主机位数,网络位数,子网数
  16. 在韩国5G商用神话中,我们不能学到什么?
  17. 腾讯安全与青藤云安全合作升级,助力客户完成年度大型攻防实战
  18. 一代宗师陨落!84岁华人计算机视觉泰斗Thomas S. Huang 仙逝,李飞飞等沉痛悼念黄煦涛教授...
  19. 图像分割中阈值的自动选取的研究及其算法实现
  20. 【随笔】孤独前是迷茫,孤独后是成长

热门文章

  1. response详解
  2. 如何使用latex排版并排放置两张图
  3. mysql fast shutdown_MySQL参数解析 innodb_fast_shutdown
  4. java adt教程_ADT工具使用详解
  5. Java开发上门洗车系统源码小程序app介绍
  6. Incremental SfM
  7. 第七周----4.13
  8. oracle listagg如何去重
  9. [2021]Zookeeper getAcl命令未授权访问漏洞概述与解决
  10. headers.Authorization = `Bearer ${token}` 为什么要加Bearer