当前位置: 首页 > 编程日记 > 正文

深入浅出Rust Future - Part 1

本文译自Rust futures: an uneducated, short and hopefully not boring tutorial - Part 1,时间:2018-12-02,译者:
motecshine, 简介:motecshine

欢迎向Rust中文社区投稿,投稿地址,好文将在以下地方直接展示

  1. Rust中文社区首页
  2. Rust中文社区Rust文章栏目
  3. 知乎专栏Rust语言
  4. sf.gg专栏Rust语言

Intro

如果你是一个程序员并且也喜欢Rust这门语言, 那么你应该经常在社区听到讨论Future 这个库的声音, 一些很优秀的Rust Crates都使用了Future 所以我们也应该对它有足够的了解并且使用它. 但是大多数程序员很难理解Future到底是怎么工作的, 当然有官方 Crichton's tutorial这样的教程, 虽然很完善, 但我还是很难理解并把它付诸实践.

我猜测我并不是唯一一个遇到这样问题的程序员, 所以我将分享我自己的最佳实践, 希望这能帮助你理解这个话题.

Futures in a nutshell

Future 是一个不会立即执行的特殊functions. 他会在将来执行(这也是他被命名为future的原因).我们有很多理由让future functions来替代std functions,例如: 优雅性能可组合性.future的缺点也很明显: 很难用代码去实现. 当你不知道何时会执行某个函数时, 你又怎么能理解他们之间的因果关系呢?

处于这个原因, Rust会试图帮助我们这些菜鸟程序员去理解和使用future这个特性。

Rust's futures

Rust 的futures 总是一个Results: 这意味着你必须同时指定期待的返回值和备用的错误类型。 让我们先简单的实现一个方法,然后把它改造成future. 我们设计的这个方法返回值是 u32 或者是一个 被Box包围着的Error trait, 代码如下所示:

fn my_fn() -> Result<u32, Box<Error>> { Ok(100) 
} 

这段代码很简单,看起来并没有涉及到future. 接下来让我们看看下面的代码:

fn my_fut() -> impl Future<Item = u32, Error = Box<Error>> { ok(100) 
} 

注意这两段代码不同的地方:

  1. 返回的类型不再是Result而是一个impl Future. Rust Nightly版本是允许我们返回一个future的。
  2. 第二个函数返回值的参量Item = u32, Error = Box<Error>较第一个函数来看更加详细明确。
为了能让第二段代码工作 你需要使用拥有conservative_impl_trait特性的nightly版本。当然,如果不嫌麻烦,你可以使用boxed trait来替代。

另请注意第一个函数返回值使用的是大写的Ok(100)。 在Result函数中,我们使用大写的Ok枚举,而future我们使用小写的ok方法.

规则: 在Rustfuture中使用小写返回方法ok(100).

好了现在我们改造完毕了,但是我们该怎样执行第二个我们改造好的方法?标准方法我们可以直接调用,但是这里需要注意的是地一个方法返回值是一个Result, 所以我们需要使用unwrap()来获取我们期待的值。

let retval = my_fn().unwrap(); 
println!("{:?}", retval); 

由于future在实际执行之前返回(或者更准确的说, 返回的是我们将来要执行的代码), 我们需要一种途径去执行future。为此我们使用Reactor。我们只需要创建一个Reactor并且调用他的run方法就可以执行future. 就像下面的代码:

let mut reactor = Core::new().unwrap(); 
let retval = reactor.run(my_fut()).unwrap(); 
println!("{:?}", retval); 

注意这里我们unwrap的是run方法,而不是my_fut. 看起来真的很简单。

Chaining

future一个很重要的特性就是能够把其他的future组织起来形成一个chain. 举个栗子:

你邀请你的父母一起吃晚饭通过email.
你在电脑前等待他们的回复
父母同意与你一起吃晚饭(或者因为一些原因拒绝了)。

Chaining就是这样的,让我们看一个简单的例子:

fn my_fn_squared(i: u32) -> Result<u32, Box<Error>> { Ok(i * i) 
} fn my_fut_squared(i: u32) -> impl Future<Item = u32, Error = Box<Error>> { ok(i * i) 
} 

现在我们可以使用下面的方式去调用这两个函数:

let retval = my_fn().unwrap(); 
println!("{:?}", retval); 
let retval2 = my_fn_squared(retval).unwrap(); 
println!("{:?}", retval2); 

当然我们也可以模拟Reactor来执行相同的代码:

let mut reactor = Core::new().unwrap(); 
let retval = reactor.run(my_fut()).unwrap(); 
println!("{:?}", retval); 
let retval2 = reactor.run(my_fut_squared(retval)).unwrap(); 
println!("{:?}", retval2); 

但还有更好的方法,在Rust中future也是一个trait他有很多种方法(这里我们会介绍些),其中一个名为and_then的方法,在语义上完全符合我们最后写的代码片段。但是没有显式的执行Reactor Run, 让我们一起来看看下面的代码:

let chained_future = my_fut().and_then(|retval| my_fut_squared(retval));
let retval2 = reactor.run(chained_future).unwrap(); 
println!("{:?}", retval2); 

让我们看看第一行:创建一个被叫做chained_futurefuture, 它把my_futmu_fut_squared`future串联了起来。 这里让人难以理解的部分是: 我们如何将上一个future的结果传递给下一个future`?

在Rust中我们可以通过闭包来捕获外部变量来传递future的值。 可以这样想:
  1. 调度并且执行my_fut()
  2. my_fut()执行完毕后,创建一个retval变量并且将my_fut()的返回值存到其中。
  3. 现在将retval作为my_fn_squared(i: u32)的参数传递进去,并且调度执行my_fn_squared
  4. 把上面一些列的操作打包成一个名为chained_future的调用链。

第二行代码,与之前的相同: 我们调用Reactor run(), 要求执行chained_future并给出结果。 当然我们可以通过这种方式将无数个future打包成一个chain, 不要去担心性能问题, 因为future chainzero cost.

RUST borrow checked可能让你的future chain 写起来不是那么的轻松,所以你可以尝试move你的参数变量.

Mixing futures and plain functions

你也可以使用普通的函数来做future chain, 这很有用, 因为不是每个功能都需要使用future. 此外, 你也有可能希望调用外部你无法控制的函数。 如果函数没有返回Result,你只需在闭包中添加函数调用即可。 例如,如果我们有这个普通函数:

fn fn_plain(i: u32) -> u32 { i - 50  
} let chained_future = my_fut().and_then(|retval| { let retval2 = fn_plain(retval); my_fut_squared(retval2) 
}); 
let retval3 = reactor.run(chained_future).unwrap(); 
println!("{:?}", retval3); 

如果你的函数返回Result则有更好的办法。我们一起来尝试将my_fn_squared(i: u32) -> Result<u32, Box<Error>方法打包进future chain

在这里由于返回值是Result所以你无法调用and_then, 但是future有一个方法done()可以将Result转换为impl Future.这意味着我们可以将普通的函数通过done方法把它包装成一个future.

let chained_future = my_fut().and_then(|retval| { done(my_fn_squared(retval)).and_then(|retval2| my_fut_squared(retval2)) 
}); 
let retval3 = reactor.run(chained_future).unwrap(); 
println!("{:?}", retval3); 

注意第二:done(my_fn_squared(retval))允许我们在链式调用的原因是:我们将普通函数通过done方法转换成一个impl Future. 现在我们不使用done方法试试:

let chained_future = my_fut().and_then(|retval| {my_fn_squared(retval).and_then(|retval2| my_fut_squared(retval2)) 
}); 
let retval3 = reactor.run(chained_future).unwrap(); 
println!("{:?}", retval3); 

编译不通过!

Compiling tst_fut2 v0.1.0 (file:///home/MINDFLAVOR/mindflavor/src/rust/tst_future_2) 
error[E0308]: mismatched types 
--> src/main.rs:136:50 | 136 | my_fn_squared(retval).and_then(|retval2| my_fut_squared(retval2)) | ^^^^^^^^^^^^^^^^^^^^^^^ expected enum `std::result::Result`, found anonymized type | = note: expected type `std::result::Result<_, std::boxed::Box<std::error::Error>>` found type `impl futures::Future` 
error: aborting due to previous error 
error: Could not compile `tst_fut2`. 

expected type std::result::Result<_, std::boxed::Box<std::error::Error>> found type impl futures::Future,这个错误有点让人困惑. 我们将会在第二部分讨论它。

Generics

最后但并非最不重要的, futuregeneric(这是啥玩意儿啊)一起工作不需要任何黑魔法.

fn fut_generic_own<A>(a1: A, a2: A) -> impl Future<Item = A, Error = Box<Error>> where A: std::cmp::PartialOrd, { if a1 < a2 { ok(a1) } else { ok(a2) } 
} 

这个函数返回的是 a1 与 a2之间的较小的值。但是即便我们很确定这个函数没有错误也需要给出Error,此外,返回值在这种情况下是小写的ok(原因是函数, 而不是enmu)

现在我们调用这个future:

let future = fut_generic_own("Sampdoria", "Juventus"); 
let retval = reactor.run(future).unwrap(); 
println!("fut_generic_own == {}", retval); 

阅读到现在你可能对future应该有所了解了, 在这边文章里你可能注意到我没有使用&, 并且仅使用函数自身的值。这是因为使用impl Future,生命周期的行为并不相同,我将在下一篇文章中解释如何使用它们。在下一篇文章中我们也会讨论如何在future chain处理错误和使用await!()宏。

相关文章:

cmd 修改文件属性

现在的病毒基本都会采用一种方式&#xff0c;就是将病毒文件的属性设置为系统隐藏属性以逃避一般用户的眼睛&#xff0c;而且由于Windows系统的关系&#xff0c;这类文件在图形界面下是不能修改其属性的。但是好在Windows还算做点好事&#xff0c;留下了一个attrib命令可以让我…

Django 视图

Django之视图 目录 一个简单的视图CBV和FBV FBV版&#xff1a;CBV版&#xff1a;给视图加装饰器 使用装饰器装饰FBV使用装饰器装饰CBVrequest对象 请求相关的常用值属性方法Response对象 使用属性JsonResponse对象Django shortcut functions render()redirect()Django的View&am…

喜大普奔!GitHub官方文档推出中文版

原创整理 | Python开发者&#xff08;ID&#xff1a;PythonCoder&#xff09;最近程序员交友圈出了一个大新闻&#xff0c;GitHub 帮助文档正式推出中文版了&#xff0c;之前一直都是只有英文文档&#xff0c;看起来费劲不方便。这份中文文当非常详尽&#xff0c;可以说有了它 …

Linux中获取当前程序路径的方法

1、命令行实现&#xff1a;转自&#xff1a;http://www.linuxdiyf.com/viewarticle.php?id84177 #!/bin/sh cur_dir$(pwd) echo $cur_dir 注意&#xff1a;在cur_dir后没空格&#xff0c;后面也不能有空格&#xff0c;不然它会认为空格不是路径而报错 2、程序实现&#xf…

android 关于字符转化问题

今日在写android的客户端&#xff0c;发现字符转化是个大问题。 下面是Unicode转UTF-8的转化&#xff0c;便于以后使用 private static String decodeUnicode(String theString) { char aChar; int len theString.length(); StringBuffer outBuffer new Strin…

30分钟看懂XGBoost的基本原理

作者 | 梁云1991转载自Python与算法之美&#xff08;ID: Python_Ai_Road&#xff09;一、XGBoost和GBDTxgboost是一种集成学习算法&#xff0c;属于3类常用的集成方法(bagging,boosting,stacking)中的boosting算法类别。它是一个加法模型&#xff0c;基模型一般选择树模型&…

Linux下遍历文件夹的实现

转自&#xff1a;http://blog.csdn.net/wallwind/article/details/7528474 linux C 遍历目录及其子目录 #include <stdio.h> #include <string.h> #include <stdlib.h> #include <dirent.h> #include <sys/stat.h> #include <unistd.h&…

如何用Python画一棵漂亮的树

Tree海龟绘图turtle 在1966年&#xff0c;Seymour Papert和Wally Feurzig发明了一种专门给儿童学习编程的语言——LOGO语言&#xff0c;它的特色就是通过编程指挥一个小海龟&#xff08;turtle&#xff09;在屏幕上绘图。 海龟绘图&#xff08;Turtle Graphics&#xff09;后来…

windows7下,Java中利用JNI调用c++生成的动态库的使用步骤

1、从http://www.oracle.com/technetwork/java/javase/downloads/jdk-7u2-download-1377129.html下载jdk-7u2-windows-i586.exe&#xff0c;安装到D:\ProgramFiles\Java&#xff0c;并将D:\ProgramFiles\Java\jdk1.7.0_02\bin添加到环境变量中&#xff1b; 2、从http://www.ec…

外观模式 - 设计模式学习

外观模式(Facade)&#xff0c;为子系统中的一组接口提供一个一致的界面&#xff0c;此模式定义了一个高层接口&#xff0c;这个接口使得这一子系统更加容易使用。 怎么叫更加容易使用呢&#xff1f;多个方法变成一个方法&#xff0c;在外观看来&#xff0c;只需知道这个功能完成…

Google最新论文:大规模深度推荐模型的特征嵌入问题有解了!

转载自深度传送门&#xff08;ID: gh_5faae7b50fc5&#xff09;导读&#xff1a;本文主要介绍下Google在大规模深度推荐模型上关于特征嵌入的最新论文。 一、背景大部分的深度学习模型主要包含如下的两大模块&#xff1a;输入模块以及表示学习模块。自从NAS[1]的出现以来&#…

[20181204]低版本toad 9.6直连与ora-12505.txt

[20181204]低版本toad 9.6直连与ora-12505.txt--//我们生产系统还保留有一台使用AMERICAN_AMERICA.US7ASCII字符集的数据库,这样由于toad新版本不支持该字符集的中文显示.--//我一直保留toad 9.6的版本,并且这个版本是32位的,我必须在我的机器另外安装10g 32位版本的客户端,这样…

Google揭露美国政府通过NSL索要用户资料

当美国联邦调查局FB或其他美国执法机构进行有关国家安全的调查时&#xff0c;能通过一种“国家安全密函National Security &#xff0c;NSL)”向服务商索取其用户的个人资料&#xff0c;由于事关国家安全&#xff0c;因此该密函并不需经法院同意。但根据美国电子通讯隐私法的规…

Ubuntu下,Java中利用JNI调用codeblocks c++生成的动态库的使用步骤

1、 打开新立得包管理器&#xff0c;搜索JDK&#xff0c;选择openjdk-6-jdk安装&#xff1b; 2、 打开Ubuntu软件中心&#xff0c;搜索Eclipse&#xff0c;选择Eclipse集成开发环境&#xff0c;安装&#xff1b; 3、 打开Eclipse&#xff0c;File-->New-->Java Proj…

比Hadoop快至少10倍的物联网大数据平台,我把它开源了

作者 | 陶建辉转载自爱倒腾的程序员&#xff08;ID: taosdata&#xff09;导读&#xff1a;7月12日&#xff0c;涛思数据的TDengine物联网大数据平台宣布正式开源。涛思数据希望尽最大努力打造开发者社区&#xff0c;维护这个开源的商业模式&#xff0c;他们相信不将最核心的代…

Script:挖掘AWR实现查询SCN历史增长走势

AWR中记录了快照时间内calls to kcmgas的统计值&#xff0c;calls to kcmgas的意义在于通过递归调用获得一个新的SCN&#xff0c;该统计值可以看做SCN增长速度的主要依据&#xff0c;通过挖掘AWR可以了解SCN的增长走势&#xff0c;对于我们诊断SCN HEADROOM问题有所帮助&#x…

运动目标检测__光流法

以下内容摘自一篇硕士论文《视频序列中运动目标检测与跟踪算法的研究》&#xff1a; 1950年Gibson首先提出了光流的概念&#xff0c;光流(optical flow)法是空间运动物体在观测成像面上的像素运动的瞬时速度。物体在运动的时候&#xff0c;它在图像上对应点的亮度模式也在做相…

读完这45篇论文,“没人比我更懂AI了”

作者 | 黄海广 转载自机器学习爱好者&#xff08;ID:ai-start-com&#xff09; 导读&#xff1a;AI领域的发展会是IT中最快的。我们所看到的那些黑科技&#xff0c;其后无不堆积了大量论文&#xff0c;而且都是最新、最前沿的论文。从某种角度来讲&#xff0c;它们所用的技术跟…

深入理解JVM——虚拟机GC

对象是否存活 Java的GC基于可达性分析算法(Python用引用计数法)&#xff0c;通过可达性分析来判定对象是否存活。这个算法的基本思想是通过一系列"GC Roots"的对象作为起始点&#xff0c;从这些节点开始向下搜索&#xff0c;搜索所走过的路径称为引用链&#xff0c;当…

​2019年最新华为、BAT、美团、头条、滴滴面试题目及答案汇总

作者 | 苏克1900来源 | 高级农民工&#xff08;ID&#xff1a;Mocun6&#xff09;【导语】最近 GitHub 上一个库火了&#xff0c;总结了 阿里、腾讯、百度、美团、头条等国内主流大厂的技术面试题目&#xff0c;目前 Star 2000&#xff0c;还在持续更新中&#xff0c;预计会火下…

华胜天成ivcs云系统初体验2

重启完成以后&#xff0c;就看到传统的linux init3级别的登录界面。输入用户名root 密码&#xff1a;123456 &#xff08;默认&#xff09;接下来的工作是配置一些东西&#xff0c;让它跑起来。首先&#xff0c;要修改IP地址&#xff0c;还有机器名。输入命令&#xff1a;ivcs…

OpenCV中响应鼠标信息cvSetMouseCallback函数的使用

转自&#xff1a;http://blog.csdn.net/haihong84/article/details/6599838 程序代碼如下&#xff1a; #include <cv.h> #include <highgui.h> #include <stdio.h void onMouse(int event,int x,int y,int flags,void* param ); int main(int argc, char** …

日常遇到的一些问题或知识的笔记(一)

1、坑爹的sessionStorage 一直以为sessionStorage、localStorage跟cookie一样&#xff0c;只要存在&#xff0c;整个域名下都可见&#xff0c;直到新开了一个窗口tab页&#xff0c;惊奇的发现下面的sessionStorage丢失了&#xff01; Web Storage 包含如下两种机制&#xff1a;…

你是“10倍工程师”吗?这个事,​国外小伙伴们都快“吵”起来了

整理 | 夕颜出品 | AI科技大本营&#xff08;ID:rgznai100&#xff09;【导读】近日&#xff0c;推特上一个话题“10x工程师”异常火爆&#xff0c;引发的热议经久不散。这个话题由一位印度初创公司投资人 Shekhar Kirani 的一条推特引发&#xff0c;他写道&#xff1b;“如果…

运动目标跟踪__kalman

转自&#xff1a;http://blog.csdn.net/lindazhou2005/article/details/1534234 1、 什么是卡尔曼滤波器&#xff08;What is the Kalman Filter?&#xff09; 在学习卡尔曼滤波器之前&#xff0c;首先看看为什么叫“卡尔曼”。跟其他著名的理论&#xff08;例如傅立叶变换&a…

Spring工厂常识

环境搭建导入Sring对应的jar包导入Spring依赖的commons-loggin包导入log4j.properties在src下导入ApplicationContext.xml在任意目录下是一个轻量级的企业开发框架核心:IOC , AOP编程IOC:也就是inverse of control 控制反转 就是讲创建对象的权利转移到工厂中,从而实现解耦合和…

iframe子页面操作父页面

2019独角兽企业重金招聘Python工程师标准>>> 最近经常用到iframe&#xff0c;用的最多的就是在子页面中操作父页面的方法或变量等&#xff0c;总结了用到的几种方法&#xff0c;如下&#xff1a; var tableName window.parent.frames["mainFrame"].tNam…

ASP.NET MVC动作过滤器

ASP.NET MVC中包含以下4种不同类型的Action Filter&#xff1a; 类型使用时机接口实现方法授权过滤器(Authorization Filter)在执行任何Filter或Action之前被执行&#xff0c;用于进行身份验证IAuthorizationFilterAuthorizeAttribute动作过滤器(Action Filter)在执行Action之前…

什么限制了GNN的能力?首篇探究GNN普适性与局限性的论文出炉!

作者 | Andreas Loukas译者 | 凯隐责编 | Jane出品 | AI科技大本营&#xff08;ID: rgznai100&#xff09;【导读】GNN是目前机器学习领域的热门网络之一&#xff0c;肯多研究与技术分享相比不可知的深度学习网络模型&#xff0c;GNN 有哪些吸引我们的优势及硬核实力。然而&…