为什么异步生锈不起作用

2021-03-11 01:10:44

2017年,我说“异步防锈编程是一场灾难和一团糟”。在2021A中,更多的铁锈生态系统变得异步 - 这可能是适当的只是说Rustrogramming现在是一场灾难和一团糟。作为曾经真正爱过生锈的人,这让我很伤心。

我觉得这个,我会试图解释我们在这里的方式。许多人已经解释了异步编程的问题 - 例如,着名的颜色是你的函数。 1但是,我认为有许多特定于生锈设计的东西,使异步源特别杂乱,最重要的是做出任何类型的异步编程所固有的问题。

特别是,我实际上认为Rust的设计几乎与许多异步范例根本不兼容。这并不是那些设计的人在他们的工作中是无能的或坏的 - 他们实际上给予了令人惊讶的好工作! 2我只是不认为它永远都会干净地锻炼 - 并看看为什么,你将不得不读取一些长的博客文章!

我想制作一个简单的功能,在后台做一些工作,让我们知道它是通过运行另一个背景工作的结果来完成的。

使用std :: thread; ///做一些艰苦的工作"异步地和#34;,并且在完成后用///的结果呼叫`func`。 fn do_work_and_then(func:fn(i32)){thread :: spawn(move || {//计算出生命的含义...线程:: sleep_ms(1000); // gee,这需要时间.. 。//啊,它' s'让结果:i32 = 42; //让' s调用`func`并告诉它好消息...... func(结果)}); }

有这个想法称为“一流的函数”,称你绕过函数,就像它们是对象一样。这将是生锈,对吗?

看那个Func:fn(i32)? fn(i32)是一个统一I32的函数的类型,并没有返回任何函数。谢谢到一流的函数,我可以通过函数来​​do_ward_and_then指定在我的工作中ondone之后应该发生的是什么 - 如此:

fn main(){do_work_and_then(|意思_of_life | {println!("哦,我找到它:{}",digen_of_life);}; //做其他东西线程:: sleep_ms(2000); }

因为do_work_and_then是异步的,它立即返回它的东西在后台,因此main的控制流程不会中断。我可以做一些其他形式的工作,这会很好(但在这里我只等2秒,因为没有什么比这更好的了)。同时,当我们弄清楚生活的意义时,它会被打印出来。实际上,如果您运行此程序,您可以得到:

这真是令人兴奋;我们可以构建整个Web服务器和网络的东西,无论如何!让我们是一个更高级的例子:我有一个数据库,我想在找到它的时候存储生命的意义,然后我可以在前台运行Web服务器,使人们能够在我完成后获得它(并返回如果我还没有完成一些错误)。

struct数据库{data:vec< I32> } Alcm Database {Fn Store(& mut self,data:i32){self .data .push(数据); fn main(){let mut db = database {data:vec! []; do_work_and_then(| tapt_of_life | {println!("哦,我发现它:{}",digen_of_life); db .store(意大图_of_life);}); // i' d如果我真的在制作一个web服务器,请在这里从`db读取。 //,但它在点旁边,所以我不去。 //(也必须在`电弧< t>>`)线程:: sleep_ms(2000); }

错误[E0308]:不匹配类型 - > SRC / MAIN.RS:27:22 | 27 | do_work_and_then(|意思_of_life | {| ______________________ ^ 28 | | Println!("哦,我找到它:{}",意思_of_life); 29 | | db.store(意思_of_life); 30 | |} ); | | _____ ^预期的fn指针,找到闭合| =注意:预期FN指针`Fn(i32)`找到关闭`[closure@src/main.rs:27:22:30:6]`

所以,这实际上非常复杂。之前,我们传递给do_work_and_then的函数是纯粹的:它没有任何数据,所以你可以像函数指针一样传递它(fn(i32)),并且所有都是grand.wever,这个新的函数在这个榜样是一个关闭:一个函数对象,其中一点数据(a& mut数据库)加到它上。

关闭是魔法。我们实际上无法命名他们的类型 - 如上所示,rust编译器称为它[closue@src/main.rs:27:22:30:6],但我们实际上无法在有效的生锈代码中写入。如果你要在外面写它,一个关闭会看起来像这样:

结构闭合< ' a> {数据:& ' mut数据库,func:fn(i32,& mut数据库)} iclip< ' a>关闭< ' a> {fn呼叫(& mut self,arg:i32){(self .func)(arg,self .data)}}

能够命名生锈类型非常重要。常规旧类型,像U8,生活很容易。我可以写一个函数fn add_one(:u8) - > U8需要一个并返回一个没有任何麻烦的人。

如果您无法实际命名类型,请使用它变得有些繁琐。您最终不得不代替使用泛型的东西 - 例如,封闭类型无法直接命名,但由于它们实现了一个fnfamily的特征之一,我可以编写以下功能:

如果我想用struct或其他东西将它们存储,我还需要每次被用来与where子句一起做这个舞蹈。这很烦人,让我更加困难,但它仍然含糊不清。目前。

[图片:来自MSQL-SRV箱,显示了使用封闭件结果的许多子句的示例]

设计的方式往往会鼓励某些模式,同时劝阻他人。由于所有权和生命,拥有对其他数据块的引用的数据变为问题。如果我的类型有一个&或者一个&捣蛋到某种东西,生锈让我确保:

有问题的东西概述了我的类型;你不能去除我所指的东西,如果istill有一个引用,我的参考是无效的

我对某些东西的提及不会与其他引用相冲突(例如,我不能拥有我的&& Ifsomething其他有A& mut参考)

因此,它们的参考资料几乎是“放射性”;您可以将它们保留一下(例如在一个特定功能内),但试图使它们长期以来通常是有点问题(需要提前技巧,例如引脚< t>甚至没有存在的类型很少有锈版版本前)。当你使用射入型号太久时,生锈并不喜欢它 - 他们让借用检查员不安,因为你延长了一段时间。

关闭可以是放射性的。看看我们刚刚写的东西:它有一个&' ut数据库引用它!这意味着在我们传递我们的关闭对象时,我们必须注意三个规则(旅行,并不是“T搬家,没有冲突) - 这使得事情变得非常难。我不能只用封闭关闭另一个函数(例如,do_work_and_then函数),因为那么我必须制作所有这些规则工作,这一切都不一定很容易。

(并非所有闭包都是放射性的:如果您让它们移动闭合,则它们将按价值取得的一切,而且群集对象拥有数据,而不是对数据进行放射性引用。更多的痛苦来处理,但你失去了蓝色辐射发光,当你看着它们时,东西会发出。)

另外,请记住我所说的能够命名类型?我们实际上并没有处理一个漂亮的,写的封闭性质;我们正在处理为我们生成的编译器,我们不能命名,这很烦人。我也撒谎,当我说它像制作你的所有功能一样简单,其中f:fn(i32)或某物 - 实际上有三种不同的fn风格的特征,fn,fnmut和fnonce。你知道它们之间的区别吗?

所以。关闭是这种神奇的,不可最可爱的类型,编译器在我们使用时为我们提供|| {...}语法,它的含义三个特征(它没有立即显而易见的),它也可能是放射性的.try和使用其中一个,而且生锈编译器可能会非常谨慎地看着你。

我真正想要尝试在这里尝试的事情是Rust不是一种语言,其中一流的功能是符合人体工程学的。它更容易制作一些数据(方法)附加的一些函数(方法)而不是制作有些功能与有一天附着(闭包)。

您选择在它们上实施的特征和方法以及如何将它们放出/实现它们

结构实际上可以通过其类型的代码的其他部分引用

它可能是放射性的(或强制您使用移动,也可能插入一堆克隆()呼叫)3

您实际上无法在任何地方命名或做的事情,如从函数返回它们

重要的是,应用于使用包含它们的闭包的限制 - 如果您正在编写包含的类型,则必须使其在某些FN特征实现类型参数上泛型,并且它将是不可能的为结果命名您的类型。

(其他语言,如haskell,翻转这个倒置:功能无处不在,你可以通过鲁莽的放弃等。课程,这些其他语言通常有垃圾收集,使其全部工作......)

考虑到这一点,它真的很难制作很多异步范式(如异步/等待)在Rust中工作。你的职能帖子的颜色是什么,异步/等待(以及承诺等事情,回调和期货)真的是延续传递风格的大抽象 - 一个与方案编程语言感到平时的想法。基本上,你的想法是你的常规,花园 - 品种的功能,并将其涂抹成一堆封闭。(嗯,不完全。你可以读取更多的蓝色链接;我不会在这里解释CPS简洁。)

希望现在你可以看到让一堆关闭真的不会是一个好主意(!)

然后快进了几年,你有一个完整的语言生态系统,基于制作这些未来的Objectsthat的概念之上,实际上有一个封闭封闭式封闭件,上面列出的所有问题(难以命名,可以包含哪些引用使它们放射性,通常需要使用通用的Where子句等)适用于它们,因为如何“传染性”闭包。

通过引入ichar特性和异步Fn等功能,这种语言的人实际上是艰难的atwork来解决一些(一些!)这些问题,如ichar特和异步fn,使得与thesenot立即完全可怕,但试图使用其他语言特征(如特征)很快清楚问题,问题并非真正消失;只是隐藏了。

哦,所有颜色的问题都是你的功能,顺便说一下,在特定于防锈的方面也是如此。

初学者(和经验丰富的)铁锈程序员看世界的状态,并尝试并在这些shakyabstractions之上建立东西,并最终跑进模糊的编译器错误,并使用像异步_traitcrate这样的黑客粘合在一起,结束依赖于像3种不同版本的Tokio和期货(如果你感到辛辣),那里有3种不同版本的项目,因为人们对如何尝试和避免从根本上不可避免的问题有不同的意见,这一切都有点令人沮丧并且最终,一切都很伤心。

它真的必须以这种方式结束吗?在大多数情况下旋转一堆操作系统线路我们可以更喜欢解决的解决方案,其中一个语言提供的运行时使阻止更多的事情要做?

也许我们可以像2016年大约在大约十时候一样生锈,让疯狂的无阻塞人为5写作制作的ePOLS()循环,就像他们在C ++中一样。老实说,我不知道,认为这是一个难以解决的问题。

但就我的钱而言,我发现当生态系统这样的时候,我发现很难证明在生锈中启动新项目。 Asi在开始时说,这让我有点难过,因为我真的像生锈。

(但常见的LISP非常好。我们有疯狂的宏和括号,以及一种语言生态系统,它比我的isolder和没有显示任何改变的迹象......)

如果您尚未熟悉它,这是真的建议的阅读(因为你很快看到)♥

严重 - 当我拨出最后一个博客帖子时,实际的异步核心团队成员评论说他们有多普赏的反馈,然后他们实际上是最好的1.0。荣誉! ↩

你可能会想到“好吧,你为什么不用移动封闭件?” - 但这就是在旁边;这样做通常很难,因为现在您可能必须将数据包装在弧形或其他东西中,所以通过借用检查者诱导的疼痛超过了使用闭包的符合人体工程学的痛苦。 ↩

您实际上可以在常规旧结构上手动实施未来。如果您这样做,事情突然变得更简单,但您也无法轻松在该结构的方法内执行更多的异步操作。 ↩

(对不起,我的意思是,为他们的低延迟生产服务使用Rust的尊敬的公司)↩