瞄准类型的正确性

2020-12-13 06:32:15

《自然》科学周刊于1869年首次出版。一个半世纪之后,它通过发表一篇有关Rust编程语言的文章,终于完成了一个癌变周期。

我对本文的喜欢之处在于,它不仅谈论性能,甚至谈论内存安全,还谈论了正确性。

好吧,它也谈到了多样性和包容性,我认为这也是极其重要的,但这不是语言的内在品质,更不是一种事态-我们不能认为这是人类动态的本质他们是...动态的。

这并不是说,Rust周围社区的质量,即那些构建,使用和教Rust的人,都不会影响语言本身的质量。恰恰相反。我的意思是,如果我们不小心,社区会迅速退化,尤其是随着语言的广泛采用。

对!它不像一个坏苹果那么简单,破坏了主持人的倦怠。

话虽这么说-我现在(可能永远)都没有特别资格来详细讨论该主题,这就是为什么今天,我将继续专注于正确性的概念。

每当出现Rust的主题时,通常都会与其他语言进行比较。在很多时候(引起社区中许多人的困扰),对话演变成一系列争论,说明为什么应该在Rust中编写(或重写)某些软件。

这种模式是如此普遍,它已成为一个模因-它有自己的首字母缩写:RIIR,用于"在Rust中重写它。如果您将这些词放在搜索引擎中,您会发现文章不乏解释为什么应该-或不应该-RIIR的文章。

但是,除了它们的频率和长度之外,还有其他一些关于这些论点的共同点。 " RIRI"尽管他们尽了最大的努力,但另一方经常认为自己是“上等”或“精英”。

我尝试过RIIR的样式的文章使情况变得更糟,并且itdidn为我解决了问题,通常得出以下结论之一,其中一些是:&#34 ; Rust所作的承诺没有得到遵守,或者作者没有把这一切都弄错了,这两个都不是Rust的特别好选择。

我试图弄清楚Rust和福音主义到底是什么?对于那些已经使用了多年(有时几十年)的语言感到十分自在的人来说,这听起来实在是太难受了,而我已经开始对我感到非常满意的解释了。

它以语句集合的形式出现,我相信所有这些语句同时是正确的:

这有几个含义:首先,尝试复制其他语言中常见的模式通常注定会失败。这使某些人的学习经历令人沮丧,并且就其本身而言足以解释为什么我尝试了很多RIIR"的原因。文章以他们的方式结束。

致一个“局外人” (从来没有写过Rust的人),仅此句话也已使人感到优越。如果您经历了一位新经理的美妙经历,他觉得他们只需要稍微改变一下一切就可以维护自己的职位,那就可以了。

坚持一段时间后,这种感觉往往会消失。 Whatonce表现为琐碎的“呼吁权威”,为了改变而改变。最终,最终几乎所有发现都是基本变化,这是使整个系统正常工作所必需的。

有时,它们只是语言和/或其实现的当前限制。这是C ++人群中更多的东西。

可以说,生锈分析器是许多语言的部分重新实现。在rustc本身内部,有多个相同组件的并发实现。

这种说法同样会激怒函数式编程人群,他们已经迷上了语言,要求他们与传统的思维方式有所不同,有时甚至大不相同。诸如C,C ++,Java,Python,Go等语言。

"传统"在这里用引号引起来是因为当然,函数式编程语言并不是特别新近。我在某种意义上误用了过去十年中的很多职位空缺。

Haskellers说,不,可以理解,Rust不是传统的基本出发地。 #39;'实际上,命令式编程语言会着眼于它以及它的肮脏,肮脏的副作用。

我要说的是:公平。而且:新颖之处在于妥协。如果您找到一种调和两种根本不同但行之有效的方法的方法,那么您已经做出了一些新的尝试,可以解决新类别的问题,或者更容易解决旧类别的问题-无论如何: ; sworth调查。

同样,有几种方法可以误解该语句:我不相信Rust比x86汇编特别难编写。

但是,可以说,用Rust编写任何代码都比用Go或JavaScript编写更难。您可以使用一个非常好的JavaScript程序,并花费数小时来用Rust重写它,因为编译器需要您比以前更多地关心问题的各个方面。

这是一个完全公平的问题。因为在这种情况下,JavaScript程序是" complete"在与Rust相当之前,他们解决了同样的问题。当然,Rust程序可能会更快-但这足以与众不同吗?

我们可以早些时候发布JavaScript程序,获得客户,并增加收入。而且我们可能会担心"小错误"和性能困难稍后的。

随着Rust开始被广泛采用,不仅在几乎所有主要软件公司中,而且在许多较小的软件公司中,这都是一个论点,即像您和我这样的真正的实际人每天都要经历几次一周中的某天,因为我们作为一个行业在整个工作/生活平衡方面表现不佳。

除非您要执行登月任务,或者您正在编写用于自动驾驶汽车的软件,否则...好吧,实际上,您确实需要进行很多应用程序设计, 34; correct" -但请说,如果您在一家销售非必需品的公司工作。客户产品或服务(以及该行业的绝大部分),您只需要足够正确即可。

假设您正在编写音乐推荐系统。这里的正确性要求非常松懈。您只需从最常听的"中拉一下就可以了。标题数据集,然后按年份或流派模糊地分类。并不是说有人会这样做。眨眼眨眼。

重点是:如果您的建议中有50%仅与客户的切向相关,则可能不会损害利润。 Andanyway:他们在向客户付款吗?还是他们只是不时地通过将一些广告推入他们的耳朵来使之变得更加轻松?也许您只能为这些高级帐户运行真正的推荐系统。

许多软件应用程序的正确性并不那么重要。除非客户-付费客户-开始注意到,否则如果您不纠正不正确之处,会直接威胁将您留给竞争对手。

让我们来谈谈正常运行时间:服务可用的时间百分比,即“健康”。没有人会愚蠢地承诺100%的正常运行时间。我们几乎没有足够的控制权来实现99.99%的正常运行时间-我们通过构建冗余系统来做到这一点。如果某个节点无法正确处理请求,则退回到另一个节点,或使其不再旋转,请设置更多的负载均衡器,过滤出“延迟”一词。在Slack上,做任何事情!

而且,如果您确实无法实现承诺的正常运行时间,那么您仍然可以避免:您可以以信贷的形式返还客户,这实际上可以使他们的下一个账单要轻一些。

您运送的每一个不正确之处都需要付出代价。最直接的成本分摊给客户的信用额度-您实际上是在从自己的利润中拿出一大笔钱,作为对未能实现自己的目标的pen悔。

但可以修复许多"小错误"也有工程成本。如今,必须要有人处理积压的东西,冰盒或现在孩子们存放李子票的任何地方,然后才实际发送修复程序。并希望他们的修正不会引入回归。

因此,您编写测试。然后再进行一些测试。其中一些由于大量定律或类似原因而变得片状。所以你让他们失败。然后,您会在测试中发现错误,因此可以修复这些错误,但必须先修复后再修复。您的代码,使其通过测试,并引入一个错误,因为事实证明该调用始终来自内部测试,这始终是错误的。

而且,当您的工程师忙于执行所有这些操作时,他们并没有在开发新功能。原本可以在Go或JavaScript中构建的功能会更快。因此,随着其他公司的不断创新,您的公司将落在后面,这最终可能会使您失去整个市场份额。

这不是虚构的,而是在所有行业中发生的事情,只要我们拥有产业就可以了。

当然,逆向的噩梦场景也是真实的-我们都知道一个同事,根据我们自己的估计,他会花“永远”的钱。试图使事情变得正确。这也可能导致公司落后于其他公司继续创新并占领市场。

而且,如果您设法不让自己对本文的介绍所产生的困扰感到困惑,那么您可能还记得我提到过Rust是一种折衷方案,因此您很可能会误会我是试图与所有这些。

世界是一个混乱的地方。根据您的大脑对周围环境的了解程度以及当前的心理状态,世界范围可能很大,我想深深的沮丧。

据了解,在好公司中,有些事情不应该大声讨论。还是不认识您不认识的人。不和你的家人在一起。还是根本没有。

这是“社会契约”的一部分,老实说,我不记得签署,如果您要我的意见的话,这是胡说八道,但是不管怎么说-大量学者都认为这确实是一件事,所以就随它去吧。

特别是孩子们,往往对这个社会契约的模糊性感到沮丧。孩子们和内森·菲尔德(NathanFielder)的视频很少让我发笑,但由于即使在相当天真的情况下,行为举止都不像其他人期望的那样,使其他人极为不舒服。

关于这个社会契约的事情,除了定义不清和不断发展之外,还在于执行它的方式很少。

啊,再次成为一个前卫的少年,毫无疑问,这是历史上的第一次-如果我们都停止上学,他们对此无能为力。

好吧,这并不是我所设想的那样大的罢工,但是最终,学校管理部门和我都同意,最好是暂时跳过某些课程,这最终是可以解决的。

我不确定您是否从中学到了正确的教训,但是讨论激励措施可能最好再待一天。

例如,人们普遍认为,在短时间内锤击成千上万个请求的服务器是很粗鲁的,但是经过十年的时间,您就可以了。重新成为“有价值的客户”。

而且还不是全部。如果服务在端口80上侦听TCP连接,则通常会说HTTP。那个实际上是在RFC中编写的,但是同样,没有什么可以阻止您这样做,只是知道没有。

随着您获得客户,并且您的产品被更多种类的客户所使用,您往往会遇到越来越多的人,他们只是"完全无视您的假设。

让我们举一个我最喜欢的例子,看看SSH协议:当客户端连接到SSH服务器时,发生的第一件事就是服务器将其版本发送给客户端。

为什么存在协议的那一部分,很难说。大概,SSH的作者急于通过分析版本字符串(例如Server HTTP标头),为潜在的攻击者提供一种更简单的方法来测试漏洞,以测试漏洞。

但是,等等,我撒谎了!在服务器发送其版本之前,它可能会发送其他数据行。

现在,行为正常的SSH服务器通常会从文本文件发送行,因此我们将其称为“横幅消息”。或者它可以自动生成,然后我们将其称为MOTD(针对每日消息)。

但是如果您在框外思考...并且想要防止攻击者进入框内...

这也明显违反了SSH客户端和SSH服务器之间的隐式协定。这不是唯一可能发生的违规行为。例如,SSH服务器可能只需要很长时间才能接受连接(即完成TCP握手)。

但是这种违规非常普遍,它已成为一种模因,导致所有客户端默认情况下都对其进行保护。网络应用程序倾向于在操作上设置“超时”-在这种情况下,是“连接超时”。将会到期,客户端将简单地放弃,这将释放它以重试。

如果SSH服务器只是不发送任何内容,则"读取超时"可能会过期,并且再次,客户端将放弃此连接,然后重试。

在记录了SSH协议的所有四个RFC中,单词" timeout"仅提及一次,以建议服务器具有身份验证超时。没有提及连接超时,这证明了它只是其中之一,您应该知道是否要进行连接程序联网的应用程序。

如果我说你不能那样跟我说话,那么,没有什么可以阻止你继续这样跟我说话。这很粗鲁,但并非不可能。

问题?您确实只想为所有人提供ducks.example.org和giraffes.example.org,而internal.example.org仅应从公司VPN进行访问。

...直到您需要真正正确地实施它-那时,所有赌注都没有了。

...因此我们可能会试图通过解析HTTP请求来添加执行访问控制的代理。

//此代码充满了罪过-但它可以达到目的。 const net = require(" net");异步函数main(){让服务器=新网络。服务器({},onConnection);服务器。 on(" error",(err)=> {throw err;});让port = 8124;服务器。监听(端口,()=> {控制台。日志(`现在正在监听端口$ {端口}`);}); }函数onConnection(sock){(async()=> {//读取完整的HTTP / 1.1请求let buf =""; while(true){等待可读(sock); buf + = sock.read(); if(buf.endsWith(" \ r \ n \ r \ n")){break;}} buf = buf.trim(); console.log(`=== =传入HTTP请求====`); console.log(buf); console.log(`========================== ====`)); console.log(`(来自$ {JSON.stringify(sock.address())})))})()。 catch((err)=> {throw err;}); }异步函数可读(r){返回新的Promise((解析,拒绝)=> {r。一次("可读",resolve); r。一次(" error"拒绝); r。一次(" close&#34 ;,拒绝);}); } main()。 catch((err)=> {throw err;});

它可以在bash或zsh中工作-将名为domain的变量设置为internal.example.org值,然后指示curl不执行DNS查找,而是直接连接到localhost:8124,这是我们的node.js服务器侦听的地址上。

$ node index.js现在正在侦听端口8124 ====传入的HTTP请求==== GET / HTTP / 1.1主机:internal.example.org用户代理:curl / 7.73.0接受:* / * ====== ========================(来自{" address":" :: ffff:127.0.0.1& #34;," family":" IPv6"," port":8124})

第一行包含HTTP方法,路径和协议。所有后续行(直到CRLFCRLF)都用于标题。如果我们要按主机过滤,我们将要解析它们。

socket.address()的结果对我来说是出乎意料的-我并没有计划支持IPv6,所以让我们尝试禁用它:

// //新:我们将主机名指定为“ 0.0.0.0”(IPv4地址)服务器。监听(端口," 0.0.0.0",()=> {console.log(`Now listen on port $ {port}`);});

节点index.js现在正在侦听端口8124 ====传入的HTTP请求==== GET / HTTP / 1.1主机:internal.example.org用户代理:curl / 7.73.0接受:* / * ====== ========================(来自{" address":" 127.0.0.1&#34 ;, " family":" IPv4"," port":8124})

好的,因此-为了便于进行练习,我们假设只有以下地址才能访问内部网站:

因此,在给定IP地址的情况下,我们可能需要一个让我们知道是否允许访问内部网站的功能。

异步函数handleRequest(sock,有效负载){let {address} = sock。地址 ( ) ;让状态,输出; if(isAllowed(address)){status =" 200 OK" ;输出="授予访问权限!" ; } else {status =" 403 Forbidden" ;输出="禁止。" ; } 安慰 。日志(`[$ {地址}] $ {状态}`);袜子。写(`HTTP / 1.1 $ {status} \ r \ n \ r \ n`);袜子。写(`$ {output} \ n`);袜子。结束 ( ) ; }

函数onConnection(sock){(async()=> {//读取完整的HTTP / 1.1请求let buf =""; while(true){等待可读(sock); buf + = sock 。read(); if(buf。结尾于(" \ r \ n \ r \ n")){break;}} buf = buf trim();等待handleRequest(袜子,buf);} )()。 catch((err)=> {throw err;}); }

如果我们向本地主机发出请求,此处为使用IPv4的127.0.0.1,则得到200 OK:

$ domain =" internal.example.org&#34 ;; curl -v --connect-to" $ {domain}:80:localhost:8124" " http:// $ {domain}" *连接到主机名:localhost *连接到端口:8124 *正在尝试127.0.0.1:8124...*连接到localhost(127.0.0.1)端口8124( #0)> GET / HTTP / 1.1>主持人:internal.example.org>用户代理:curl / 7.73.0>接受:* / *> *将捆绑软件标记为不支持多次使用。 HTTP / 1.1 200 OK *没有块,没有封闭,没有大小。假设接近信号端<已授予访问权限!*关闭连接0

但是,如果我们向我们的LAN IP请求(此处不在192.168.x中,因为我打算在WSL 2上运行所有这些请求),则会收到403 Forbidden:

$ domain =" internal.example.org&#34 ;; curl -v --connect-to" $ {domain}:80:172.31.194.107:8124" " http:// $ {domain}" *连接到主机名:172.31.194.107 *连接到端口:8124 *正在尝试172.31.194.107:8124...*连接到172.31.194.107(172.31.194.107 )端口8124(#0)> GET / HTTP / 1.1> 主持人:internal.example.org> 用户代理:curl / 7.73.0> 接受:* / *> *将捆绑软件标记为不支持多次使用。 HTTP / 1.1 403禁止*没有块,没有关闭,没有大小。 假设接近信号端<禁止。* Closi ......