<返回更多

Rust 与信息学三定律

2021-01-20    
加入收藏
Rust 与信息学三定律

 

JAVA领域工作了10年之后,最近我一直在更认真地研究Rust。 我几乎没有开始Rust编程,但是我感到很热情:我相信Rust应该引起注意,因为这是语言平衡的转变。

tl; dr

Rust可以在嵌入式芯片,Web应用程序和分布式系统中运行。 它结合了C的速度,C是一种现代的强类型系统,可以安全地管理内存和高质量的生态系统。 它使编写并发代码变得简单而安全。 Rust社区热情,友好,非常活跃并且正在开发出色的软件。 Rust已在生产中使用,并且可以正常工作,但某些零件仍在完善中。 我要参加这个有趣的活动,不让您知道:-)

从Java到Rust

Java和Rust是非常不同的语言,据认为它们针对不同的空间。 有人问我为什么Java开发人员(无聊的开发人员)会对Rust感兴趣。 事实证明,我选择Java的理由与现在将我引向Rust的理由相同。 让我描述指导我选择语言和技术的原则:信息学的三定律!

信息学三定律

我将它们称为"信息学三定律"。 这些定律的内容可能不是很原始(不是真正的定律),但是像阿西莫夫的定律一样,它们试图找到最基本的正交公理,从中可以得出想要的特性。 它们是尽力而为的法则,因为完美是我们无法掌握的。

开始了:

第一定律意味着程序在所有情况下都应按预期方式运行。 这意味着软件应尽量避免出现错误,安全问题,并且永远不会崩溃。

第二定律意味着,程序应该经过精心设计和记录(因为必须对代码有很好的理解)和模块化,因为不能维护大型组件。 还应该有帮助开发人员维护程序的工具。 由于设计清晰并且可以熟练掌握该语言,因此部分演进不应保证完全重写,并且另一位开发人员应能够进行必要的更改。

第三定律意味着,对于最可能的输入,程序应趋向于在速度和资源消耗之间达到最佳的平衡,即根据其使用方式。 这意味着我们应该为工作使用最佳的数据结构,以最佳的权衡取舍,因为大多数时候没有全局最优,要适当地进行扩展计划,并使代码对运行它的计算机表示同情。

这三个法律共同为生机勃勃的生态系统和社区提出了要求。 开发人员不能单独编写正确,可维护和高效的程序,因为现代程序所依赖的代码比一个开发人员所能产生的更多。 他们必须重用社区提供的组件。 他们必须共享一个社区健康的信念,即社区正在成长,或者规模足够大,可以维护现有组件,并且可以构建新组件。 这使得在选择编程环境时,围绕语言的社区成为最重要的因素。

三定律的顺序

正确性无疑是我们在软件中的主要关注点。 如果某个软件不能正常工作,则不值得运行。 如果不安全,则存在安全风险。 这显然使我们的第一定律正确无误。

但是,人们可能会争论,可维护性是否应比效率更重要,反之亦然。 当完全忽略可维护性时,程序可能会获得更好的性能,而仅考虑正确性和可维护性的编程可能会导致程序完全无效。

实际上,可维护性和效率是交织在一起的:围绕高效解决方案构建好的架构。 例如,围绕Rope数据结构构建了Xi编辑器,旨在成为"未来20年"的高性能编辑器。 自上而下地设计系统可能会阻止实现有效的解决方案; 自下而上地设计它们可能会泄漏太多的实现细节,并阻止干净的,松耦合的体系结构。

不过,如果某个软件正确且高效,但无法维护,则仅是一个黑匣子。 如果我们不了解,我们是否确信它是正确的? 相反,效率低下但正确且可维护的程序可能仍然可以挽救。 可维护性成为第二定律。

定律和语言

信息学三定律仅涉及程序。 但是编程语言可以提供保证,构造,工具,执行环境,社区或生态系统,以帮助开发人员构建遵守法律的程序。 让我们看一些选项。

系统语言

C和C ++共享一些属性。 它们非常适合编写快速,占用资源少的代码。 手动内存管理将安全交给开发人员(他们几乎不记得他们离开手机的地方!)。 C没有附带电池,因此您经常需要重写数据结构; C ++如此庞然大物,每个项目都定义了他们使用或不使用语言的哪一部分(google因不使用异常而闻名)。 由于它们都不安全,因此依赖于另一个库会增加安全漏洞的风险。

Go填补了C ++吓跑开发人员的空白。 它的目标是速度快,并使使用例程轻松编写并发代码。 它是垃圾收集的,因此比C ++更安全。 它具有一个简单的类型系统,没有泛型和代数数据类型,不支持代码重用以及其他现代语言。 尽管如此,它仍然有一个非常活跃的社区,可能受到Google的光环的吸引,并跟随Docker的脚步。

JVM生态系统

Java是一种相当简单的常规语言。 Java的重用历史悠久,在整合第三方工件方面可能是冠军,有时使大型项目看起来像科学怪人的怪兽。 Java具有语言和虚拟机支持的出色工具。 现代JVM"及时"编译代码,并将其转变为高效的本机代码。 有不同的垃圾收集器提供不同的权衡。 哦,我提到过Java社区很大吗?

Kotlin试图通过减少冗长程度并提供一个更强大的,可空值安全的类型系统来提供Java"语言"的替代方案。 它主要针对JVM,并具有与Java相同的优势(也有Kotlin / Native)。 由JetBrains创建的工具显然非常出色。 现在已正式为Android开发提供支持,并且可以保留。

函数式编程语言或FP语言

Haskell和OCaml最初是作为研究项目开始的,但近年来在该行业中越来越受欢迎。 它们很安全,提供了出色的设计原语(尤其是类型类和模块)和编程模型,以我的经验,这些模型会减少错误。 它们都是垃圾-GC是为第一种FP语言LISP发明的。 特别是Haskell,它是完全纯净的,具有很大的好处:所有效果(例如IO)都明确表示为类型,这使开发人员不必担心意外发生的副作用,但会变得麻烦。 他们的社区都包括许多研究人员,可帮助建立坚实的正式基础。

还有更多语言

我不会再讨论其他所有语言。 有些人在其生态系统和社区方面具有非常明显的优势。 Python拥有出色的数据分析生态系统,Erlang帮助使用actor构建容错分布式系统,Scala是Kotlin的更老,更狂的兄弟姐妹,Clojure和Racket是现代的Lisps,而TypeScript则试图使JavaScript有意义!

第三定律的觉醒

确实有许多有趣的语言。 他们中的大多数人都有自己的长处和好主意。 他们有多少帮助愿意的开发人员遵循信息学三定律?

可维护性主要由良好的设计原语(也称为语言构造),良好的工具和社区来解决。 不同的学校对什么构成好的原语有不同的看法:我个人更喜欢现代的强类型FP语言选择的那些。

除了可维护性之外,有两种主流语言:具有手动内存管理的语言和具有垃圾回收的语言。 由于我们的开发人员并不完善,因此手动内存管理也意味着不安全,因此缺乏正确性。 在过去的25年中,垃圾收集虽然产生了开销,但在新的主流语言中已成为事实上的标准,因为安全比绝对性能更重要。

Rust是第一种提出替代方案的流行语言-自动内存管理和无垃圾收集的安全性-它带有功能强大的FP启发性设计原语,可构建高级抽象等等。

如果我们现在可以安全地构建更高效的软件,不是吗? 还是应该针对开发生产力进行优化? 我们能同时拥有吗?

一个新的挑战者来了

Rust是一种系统编程语言,运行速度极快,可防止出现段错误并保证线程安全。 (rust-lang.org)

Rust已经存在了一段时间,但是直到2015年Rust 1.0发行时才进行试验。 从那时起,该语言及其生态系统得到了发展并得到了极大的改善。 它将持续改善数年,但是核心团队致力于不破坏用户代码。

与Go一样,Rust旨在为对系统编程感兴趣的开发人员提供一种严肃的选择。 但是,它从语言研究和现有编程语言的成功经验中更多地借鉴(双关语意)。

Rust具有现代类型的系统,该系统可以自动管理内存并与边界检查一起确保安全地对其进行访问。 它具有泛型和接近Haskell类型类的特征系统。 这实现了Rust所谓的零成本抽象:拥有出色的设计不应损害性能。 James Munns在嵌入式编程方面的精彩演讲描述了Rust嵌入式工作组如何构建一组可重用的组件,以便在不牺牲执行成本的情况下在微型芯片上对各种类型的硬件操作进行抽象。

当我深入研究Rust资源时,从出色的Rust Book开始,然后在GitHub上寻找我可能感兴趣的项目,我注意到这种毫不妥协的方法对社区和生态系统产生了多大影响。 Rust开发人员旨在获得最佳的运行时效率,最准确的抽象和最强的执行安全性。 显然,它使Rust适用于低级编程,但对于许多更高层次的应用程序也非常有趣:尽管人们可以在Rust中编写linux内核模块,但它也用于构建REST Web应用程序,区块链节点甚至单页Web WebAssembly中的应用程序! 让我们再深入一点,看看它如何帮助编写守法程序。

正确性

编写正确的程序很困难。 我们要完成的任务通常含糊不清。 尽管如此,语言仍可以通过表现力十足而提供帮助,而无需程序员跳过障碍来提出问题。 Rust具有现代的语言结构:代数数据类型(在这里称为枚举),泛型,特征,类型别名,元组等。它还具有功能强大的元编程系统,该系统使用宏可以完成繁重的工作,例如生成序列化程序 ,特征实现或定义嵌入式DSL。 同样,Rust借鉴了Scheme和其后代Racket(它特别擅长构建DSL)的灵感,通过支持卫生宏来借鉴了出色的想法。

正确性的另一部分是没有不确定的行为,安全问题和坠毁风险。 Rust通过使类型系统意识到所有权来在没有垃圾收集器的情况下管理内存。 资源由一个单一的变量绑定拥有,并且可以通过将变量传递给函数来转移所有权:Rust称其为移动资源(在某种意义上类似于C ++的move语义)。

当变量绑定超出其词法范围时,它所拥有的资源将被删除-又名,已释放(可以通过将其移入遗忘而更早地删除该值)。 尽管它们可以证明是"活着的",但可以借用共享的方式不变地或以排他的方式可变地借用价值。 这类似于C ++引用,但是它允许Rust编译器证明不会对给定的数据结构进行共享,可变的访问,并且开发人员不必担心一件事。

最后但并非最不重要的一点是,Rust通过确保在线程之间不共享非线程安全值来保护开发人员免受许多并发问题的困扰。

有很多资源可以了解Rust的所有权和借用,但是我将展示一个简单的示例。 请注意,所有权的复杂性更高,特别是某些类型可以选择复制而不是移动的事实。 例如,"拥有"一个整数没有意义,因为它是如此之小以至于可以被简单地复制。 Rust编译器也在不断发展,变得更加智能,并允许更直观的代码。

在上面的代码段中,创建了一个名为s的新字符串,并将其传递给函数foo。 因为foo接受一个String而不是对String的引用,所以它获取了所有权而不是仅仅借用它:将String移到foo。 后来,仍然主要在尝试打印s时,Rust编译器会抱怨资源s已绑定,因此不再可用。 该程序将无法编译。

事实证明,foo返回一个不变的s,但是编译器不知道它,并且任何开发人员也不会只看主函数,foo的签名和String的特征。 知道这些元数据就足以知道foo拥有所有权,而我们对该资源的访问也会丢失。

为了使我们的程序编译,我们可以简单地打印newS。 Rust甚至让我们再次称它为s,这很棒,因为无论如何将s传递给foo后,s都不可用! 下面的程序先打印" Bar",然后打印" Foo"。

查看foo,它还会在名为s2的变量下创建另一个String。 超出范围时将被丢弃。 到目前为止,这看起来很像自动内存管理,用于在C ++中分配给堆栈上的结构或Java中由GC管理的引用句柄。 区别在于,资源(例如堆栈或堆分配的内存)始终只有一个所有者。

在这里,在foo中返回s2而不是s将把s2返回给调用者,程序将打印" Bar"" Bar"。 顺便说一句,如果您想知道为什么在调用println!之后s2仍然可以使用,那是因为它是println !! 只借了它! 最后,惊叹号一目了然。 是一个宏。

最后,没有必要在此处分配字符串,因为" Foo"和" Bar"常量已经在二进制文件中。 Rust可以直接指向那里并获得我们可以借用的"切片"。 我们使用称为&str的类型,它可以是借来的String或切片!

可维护性

所有权系统不仅使程序执行安全,而且我认为这也使代码更易于维护和重用。 一目了然的功能或词法范围,Rust开发人员可以确定其变量的属性。 他们可以构建API,以更准确地传达应如何使用它们,并从类型检查器中寻求帮助。

让我举一些例子。 在C语言中,编译器不知道函数返回的指针有效的时间。 在垃圾回收语言中,您可以存储函数返回的所有引用,并防止释放潜在的大块内存。 许多Java库随附文档,描述了什么时候可以安全地调用哪些方法,对象将处于有效状态的时间长短,并通过异常强制执行这些规则,或者让您处理未定义行为的后果。 当涉及多线程时,这变得更加困难。 Rust使得可以返回在编译时已知的给定生命周期内可能有效的引用,提供副本或提供您愿意时可以处置的共享引用。

Rust附带了Cargo,这是一个命令行实用程序,用于构建,管理依赖项(称为Crates),运行测试,修复警告等。 拥有社区认可的构建工具意味着可以将精力集中在那里。 它有助于Cargo的开发人员做出不错的选择,例如支持使用开箱即用的包装箱进行语义版本控制,使用人类可读和可编辑的配置格式(TOML)或支持可复制的版本。 还有rustfmt,这是一种自动格式化程序,可防止浪费时间手动格式化源文件以及关于tabs-vs-spaces的无休止的争论(剧透警告:赢得了4个空格)。

尽管如此,Rust的工具仍在开发中。 Java开创了20年的先机,但是语言本身非常适合工具。 IDE应该如何在宏中支持DSL? 时间会证明一切。 有一个官方语言服务器,该服务器具有VSCode和Eclipse集成。 还有一个IntelliJ IDEA插件。

Rust编译器得到LLVM的支持,LLVM是具有高效优化器的成熟基础架构。 它也可以针对WebAssembly,这使Web应用程序可以用Rust编写,并且可以允许在沙箱中运行不受信任的代码。

效率

Rust速度很快,运行速度可与C媲美。因为它没有垃圾收集器,所以没有隐藏的成本-即使没有停顿,GC代码也会在单独的线程中运行并消耗资源。

由于注重效率,因此社区很容易为所有事情运行基准。 因为代码共享既简单又安全,所以我们可以重用高性能的数据结构。 Bryan Cantrill在最近的博客中比较了程序的C版本和Rust版本,并将40%的运行时改进归因于使用了BTreeSet,这是Rust标准集合中的现成可用的高效数据结构。

Rust使用户可以控制其数据结构的内存布局,并使间接表达明确。 这不仅有助于编写缓存友好的代码,而且还可以与C接口。Rust的FFI与C一样简单,没有开销,这使得调用任何系统原语都很容易(但不安全,应适当包装)。 我们不愿意在Java中执行此操作,特别是出于稳定性方面的考虑-段错误将使JVM崩溃-但这可能很有用。 例如,最快的Java Web服务器之一正在使用JNI调用Linux的epoll,并且似乎比Java的标准无阻塞网络库NIO更好。

说到这一点,如果我们阻塞了一个等待IO的线程,那就太快了。 Rust具有零成本的期货,包括无阻塞,反压的流。 因为期货和流链接可能变得很冗长,所以可能已经使用async / await编写了惯用的Rust代码之类的异步代码。 现在,await被实现为一个宏,但是正在进行使它成为标准Rust功能的工作。

Rust的旗舰无阻塞IO库Tokio建立在期货的基础上,为无阻塞编程提供了一致且流畅的抽象。 Web框架使用Hyper HTTP库依次使用Tokio。

在我看来,Rust的核心开发人员一直在积极寻求更好的想法,这是对Sun指导Java很久的"此处未发明"综合症的一种令人耳目一新的变化。 在这些好主意中,Rust的特性和缺乏结构继承性提供了出色的设计原语,可帮助构建模块化且可维护的系统。

Rust开发人员在错误处理方面有了另一个不错的选择。 Rust具有Result <T,E>类型,可以是具有成功值的Ok(T)或具有错误的Err(E)。 Haskell程序员将识别出两种类型。 使用常规结构处理错误意味着可以使用所有常用的机制-包括模式匹配,将Result作为值传递或序列化它。

Rust还使用特质使代码不那么冗长。 像Java的Iterable及其foreach循环,或Haskell的monad表示法一样,Rust除了特质外还使用了一些健康的语法糖,可以轻松构建自然的类型。

例如,当尝试使用+操作时,将使用Rust的std :: ops :: Add特性。 运算符重载在C ++中总是带来负面影响,但这也是Python在数据分析方面如此强大的一个重要原因。 Numpy的数组和矩阵可以方便地支持我们在纸上使用的相同运算符。 为了防止冲突,Rust仅允许定义特征的模块或定义目标类型的模块实现特征。 这是进行自定义Point类型支持汇总的简单示例。

社区与生态系统

可以看到Rust社区多年来为该语言及其生态系统做出的巨大努力。 他们使从Rust开始的体验非常愉快和热情。 我发现了一种精神,使所谓的"锈病"变得动人心魄。

Rust拥有官方论坛和讨论渠道,您可以在其中获得帮助,并可以与核心开发人员讨论技术问题。 一切都是公开开发和辩论的,欢迎贡献。

每周时事通讯会提供更新和不断改进的感觉。 它选出了"每周一箱",以宣传社区的努力。 它要求就问题进行帮助,有时甚至在Rust官方发行版中也是如此。

Rust社区在GitHub上非常活跃,并将许多问题标记为任何想成为贡献者的"第一好问题"。 实际上,Rust中没有像Apache或Eclipse这样的开源基金会,但是有很强的自由软件文化。 Mozilla提供了Rust的赞助-许多核心开发人员都是Mozilla的雇员-但随后许多大型项目仍然存在于单独的GitHub帐户上。

社区仍然很紧张,每个人都在共同努力以建立一个完整的生态系统。 为了安全起见,Rust开发人员基本上重写了所有内容,以尽可能地依赖Rust代码而不是包装的C,C ++或Go库。

开发人员可以将其包装箱发布到crates.io。 Rust标准库很小,甚至是可选的,通过设计,大多数常用功能的开发(例如期货,序列化或日志记录)都发生在不同的包装箱中。 一些包装箱是通过RFC流程标准化的。

由于其品质和社区,Rust吸引了很多人才。 Rust具有出色的密码学生态系统,用于并发和数据并行性的库。 您可能对使用QUIC感兴趣吗? 有一个图书馆! 您是否在考虑Haskell的Quickcheck? 校验! 还是模糊测试? GTK +用户界面? 没问题! 您喜欢GraalVM吗? 鲁斯特有HolyJit! Nom和Pest是用于解析的两个库。 人们在Rust中编写OpenGL视频游戏,其他人则编写网络服务或WebAssembly VM。

Rust 与信息学三定律

 

未来

Rust通过启用零成本抽象并将混合好的,经过验证的想法与提供免费内存安全性的新颖方法相结合,创造了新的交易。 它通过允许高级构造来重塑系统编程,并可靠地提供了高级编程速度和控制能力。

但是,人们之所以选择语言和框架是因为它们具有生产力,并且可以保证达到特定的目的。 如果要构建Web应用程序,则Ruby on Rails或Java是安全的选择。

我个人对Java的整体工程质量,工具,生产率和舒适度感到迷恋。 Rust在那里显然不那么成熟,但是我将押注社区的动态变化,以及更多采用Rust并帮助使其成为世界一流编程环境的公司

几年后,Rust可能会提供一个生产效率高,安全的编程环境,感觉像今天的Java一样可靠且无风险。 最有趣的部分是到达那里。

 

(本文翻译自Simon Chemouil的文章《Rust and the Three Laws of Informatics》,参考:https://blog.usejournal.com/rust-and-the-three-laws-of-informatics-4324062b322b)

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>