Rust在Android中的应用:内存安全漏洞密度较C/C++降低了1000倍
我们因其安全性而采用Rust,如今发现 内存安全漏洞密度较Android的C/C++代码降低了1000倍 。但最令人惊喜的是Rust对软件交付周期的影响。Rust代码变更的 回滚率降低4倍 ,代码审查耗时 减少25%
去年我们撰文阐述了为何聚焦新代码漏洞预防的内存安全策略能快速产生持久且累积的效益。今年我们将探讨 这种方法不仅能修复问题,更能助力我们加速前进 。
2025年的数据再次验证了该策略的有效性——内存安全漏洞占比首次跌破总漏洞的20%。

我们因其安全性而采用Rust,如今发现 内存安全漏洞密度较Android的C/C++代码降低了1000倍 。但最令人惊喜的是Rust对软件交付周期的影响。Rust代码变更的 回滚率降低4倍 ,代码审查耗时 减少25% ,如今更安全的道路也成为更高效的道路。
本文将深入剖析这一转变背后的数据,并涵盖:
- 我们如何扩大覆盖范围: 正全力推动安全代码成为整个软件栈的默认标准。我们将更新Rust在第一方应用、Linux内核及固件中的采用进展。
- 我们首个Rust内存安全漏洞…险些发生: 我们将剖析一个在unsafe Rust中险些发生的内存安全漏洞:如何发生、如何缓解,以及我们为防止复发采取的措施。这也恰好能解答“既然Rust可能存在内存安全问题,何必费心使用它?”的疑问。
更快构建更优质的软件
开发操作系统需要C、C++和Rust等系统编程语言提供的底层控制与可预测性。尽管Java和Kotlin对Android平台开发至关重要,但它们与系统语言的关系是互补而非替代。我们引入Rust到Android正是为了直接替代C和C++——它提供同等控制力,却规避了诸多风险。本分析聚焦于新开发且活跃维护的代码,因数据表明此为有效方法。
在系统语言(不含Java和Kotlin)的开发趋势中,呈现出两大特征:Rust使用量急剧攀升,而新C++代码量虽缓慢但持续下降。

本图表聚焦于原生代码(谷歌开发代码),区别于前一张包含 Android 所有原生及第三方代码的图表。统计范围仅涵盖系统语言、C/C++(主要为 C++)及 Rust 语言。
图表显示,Rust新代码量现已与C++持平,这使得软件开发流程指标的可靠对比成为可能。为此我们采用DORA1框架——这项持续十年的研究计划已成为评估软件工程团队绩效的行业标准。DORA指标聚焦于:
- 吞吐量:交付软件变更的速度
- 稳定性:变更的质量
跨语言比较存在挑战性。我们采用多种技术确保比较的可靠性:
- 变更规模对等:Rust与C++的功能密度相近,但Rust略高。此差异对C++有利,但比较仍具参考价值。我们采用Gerrit变更规模定义。
- 开发者群体相似性:我们仅考虑来自 Android 平台开发者的官方变更。其中多数为谷歌软件工程师,且两类开发者群体存在大量重叠——许多人同时为两个平台贡献代码。
- 追踪趋势变化:随着Rust采用率提升,相关指标是保持稳定、加速增长还是回归均值?
吞吐量
代码审查是开发流程中耗时且延迟较高的环节。代码返工是造成这些高成本延迟的主要原因。数据显示Rust代码所需修订次数更少,这一趋势自2023年以来持续保持。同等规模的Rust代码变更所需修订次数比C++代码少约20%。
此外,当前Rust代码在代码审查环节耗时比C++少约25%。我们推测2023至2024年间Rust优势显著提升,源于Android团队Rust技术能力的增强。
虽然减少返工和加快代码审查带来了一定的生产力提升,但最显著的改进体现在变更的稳定性和质量上。
稳定性
稳定且高质量的变更使Rust脱颖而出。DORA采用回滚率评估变更稳定性。即使在Android平台上Rust的使用率已超越C++,其回滚率仍保持极低水平并持续下降。
对于中大型变更,Rust在Android平台的回滚率约为C++的1/4。 如此低的回滚率不仅体现了稳定性,更显著提升了整体开发效率。回滚操作极易破坏生产力,不仅引发组织内部摩擦,更会调动远超提交错误变更的开发者本人的资源。回滚需要返工和更多代码审查,还可能导致构建重构、事后分析以及阻塞其他团队。由此产生的事后分析往往引入新的安全措施,反而增加了开发负担。
在谷歌软件工程师的2022年自述调查中,受访者指出Rust语言既更易于代码审查,又更可能确保正确性。回滚率和代码审查时间的硬数据印证了这些感受。
综合优势
历史上,安全改进往往伴随代价:更高的安全性意味着更繁琐的流程、更低的性能或功能延迟,迫使开发者在安全与其他产品目标间权衡取舍。转向Rust则不同:我们显著提升了安全性,同时改善了关键开发效率和产品稳定性指标。
拓展应用领域
随着Rust在构建Android系统服务和库方面的支持日趋成熟,我们正致力于将其安全与生产力优势推广至其他领域。
- 内核: Android 6.12版Linux内核是我们首个启用Rust支持的内核,也是首个投入生产的Rust驱动程序。更多激动人心的项目正在推进,例如我们与 Arm 和 Collabora 共同开发的基于 Rust 的内核模式 GPU 驱动程序详见。
- 固件: 高特权、性能限制以及许多安全措施的有限适用性,使得固件既高风险又难以保障安全。将固件迁移至Rust可显著提升安全性。我们已在固件领域部署Rust多年,并向广大开发者发布了教程、 培训材料及代码示例供开源社区使用。我们尤其期待与Arm在Rusted Firmware-A项目上的合作。
- 原生应用程序: Rust正从底层保障多个谷歌关键安全应用的内存安全性,例如:
- 附近设备发现:通过蓝牙安全私密地发现本地设备的协议采用Rust实现,目前运行于Google Play服务中。
- MLS:安全RCS消息协议采用Rust实现,将在未来版本中集成至Google Messages应用。
- Chromium:针对PNG、JSON及网络字体 已替换为Rust的内存安全实现,使Chromium工程师在遵循双重规则的同时更轻松地处理网络数据。
这些案例彰显了Rust在降低安全风险中的作用,但 内存安全语言仅是全面内存安全策略的一部分 。我们持续采用深度防御策略,其价值在近期一次险些酿成事故的事件中得到充分验证。
我们首个Rust内存安全漏洞…险些发生
我们近期成功避免了首个基于Rust的内存安全漏洞——CrabbyAVIF中的线性缓冲区溢出问题。这次险情险些酿成大祸。为确保该补丁 获得高优先级处理并通过发布渠道追踪,我们为其分配了CVE-2025-48530标识符。尽管该漏洞 最终未出现在公开版本中 实属万幸,但这次险情仍带来宝贵教训。以下章节将重点阐述我们事后分析得出的关键经验。
Scudo强化分配器功不可没
一项关键发现是, Android的Scudo强化分配器通过确定性机制使该漏洞无法被利用 因其在次级分配周围设置了保护页。尽管Scudo作为Android默认分配器已在Google Pixel等众多设备上应用,我们仍持续与合作伙伴协作推动其强制部署。在此期间,对于Scudo可预防的漏洞,我们将发布足够严重程度的CVE编号。
除防范溢出攻击外,Scudo的守护页机制通过将无声内存损坏转化为有声崩溃,有效协助定位了此问题。但我们发现崩溃报告存在缺陷:未能明确标识崩溃由溢出引发,导致分类处理与响应效率降低。该问题现已修复,当溢出发生在Scudo保护页时,系统将发出明确信号。
不安全代码审查与培训
操作系统开发需要使用不安全代码(通常是C、C++或不安全的Rust代码,例如用于FFI和硬件交互),因此简单禁止不安全代码并不可行。当开发者必须使用不安全代码时,应了解如何安全负责地使用。
为此,我们正在为Rust综合培训新增深度解析不安全代码的模块。该模块旨在指导开发者如何分析不安全Rust代码的合理性与未定义行为,并传授安全注释、将不安全代码封装于安全抽象等最佳实践。
对Rust不安全代码的深入理解,将推动开源软件生态系统及Android平台实现更高质量、更安全的代码。正如下一节所述,我们的Rust不安全代码实际上已相当安全。思考安全门槛能提升到何种高度,这本身就令人振奋。
漏洞密度对比
这次险些发生的漏洞事件引发了一个必然问题:“既然Rust仍存在内存安全漏洞,那它还有什么意义?”
关键在于漏洞密度已大幅降低——这种降低程度标志着安全态势的重大转变。基于此次险情,我们可进行保守估算:Android平台约500万行Rust代码中发现(并于发布前修复)一个潜在内存安全漏洞,据此估算Rust的漏洞密度为每百万行代码(MLOC)0.2个漏洞。
历史数据显示,C/C++代码每百万行存在近1000个内存安全漏洞。而当前Rust代码的漏洞密度低了几个数量级: 减少幅度超过1000倍。
内存安全理应受到高度关注,因为该类漏洞具有独特威力且(历史上)普遍存在。高漏洞密度会破坏原本稳固的安全设计,因为这些缺陷可被串联利用以绕过防御机制,包括专门针对内存安全漏洞的防护措施。显著降低漏洞密度不仅减少了缺陷数量,更大幅提升了整个安全架构的有效性。
关于Rust的主要安全担忧通常集中在约4%的unsafe{}代码块内。这部分代码引发了大量猜测、误解,甚至出现Rust不安全代码比C更易出错的理论。实证数据表明这种观点完全错误。
我们的数据表明,即使采用更保守的假设——即每行不安全Rust代码的缺陷概率等同于C/C++代码——仍严重高估了不安全Rust的风险。具体原因尚不明确,但可能存在多重因素:
- unsafe{} 实际上并未禁用Rust全部或大部分安全检查(这是常见误解)
- 封装机制使开发者能够就局部安全不变量进行推理。
- unsafe{}代码块受到额外的审查机制。
终极思考
历史上我们不得不接受这样的权衡:缓解内存安全缺陷的风险需要在静态分析、运行时缓解措施、沙箱隔离和反应式修复方面投入大量资源。这种方法试图先快速推进,再事后收拾残局。这些分层防护固然必要,却以性能和开发者生产力为代价,且仍无法提供充分保障。
尽管C和C++仍将长期存在,软件与硬件安全机制对分层防御依然至关重要,但转向Rust语言则提供了另一种路径——这条更安全的道路也证明了更高的效率。我们无需先快速推进再事后收拾残局,而是在加速发展的同时同步完善代码。谁知道呢?随着代码安全性不断提升,或许我们还能在增强安全性的同时,重新夺回那些为安全而牺牲的性能与生产力。
鸣谢
感谢以下人士为本文提供的贡献:
- Ivan Lozano 编写了关于 CVE-2025-48530 的详细事后分析。
- Chris Ferris 验证了事后分析的结论,并据此改进 Scudo 的崩溃处理机制。
- Dmytro Hrybenko 领导了针对不安全 Rust 的培训开发工作,并为本文提供了大量反馈。
- Alex Rebert 和 Lars Bergstrom 为本文提供了宝贵建议和详尽反馈。
- Peter Slatala、Matthew Riley 和 Marshall Pierce 提供了谷歌应用中使用 Rust 的部分场景信息。
最后,衷心感谢 Android Rust 团队及整个 Android 组织对工程卓越性和持续改进的不懈追求。
本文由 TecHug 分享,英文原文及文中图片来自 Rust in Android: move fast and fix things。




核心要点。
坦白说,我认为“代码更易审查且更少需要回滚”才是本文更具价值的观点——毕竟谁不喜欢避免回滚操作呢?
安全问题同样令人头疼,只是我们早已听腻了安全说教。我更倾向于用Rust编写大部分代码。
没错,Rust的严格性让代码维护性大幅提升。修改某处时,编译器会在所有相关位置自动报错——无需检查代码库其他部分,所有测试都能一次通过。这种体验实在太常见了。
互联网上某些角落,只要提及用Rust重写代码就会招致嘲讽与愤怒。虽然技术决策本就有利有弊,但面对如此惊人的证据,我实在不明白为何有人会完全否定它。
而我这么说,其实从未写过一行Rust代码(总有一天我会抽时间试试)。
谷歌同系列的早期博文(首句链接)已阐明原因:新代码往往比成熟代码存在更多漏洞。因此用Rust编写新代码比重写旧代码更合理。毕竟新功能仍在持续添加,新代码需要编写——代码库的功能开发远未完成。
根据该博客文章(https://security.googleblog.com/2024/09/eliminating-memory-s…),Android系统中五年陈旧代码的漏洞密度仅为新代码的1/7.4。若Rust的漏洞密度低5000倍,且假设每5年重复实现7.4倍的降低幅度,则需“等待”(持续开发)约21年才能达到与全新Rust代码相同的漏洞密度。21年前是2004年——那时Android(2008年诞生)尚未问世。
>因此用Rust编写新代码比重写旧代码更合理。
这适用于任何编程语言(除非你真的退化到汇编之类的极端情况)。当然,这假设Rust(或其他新语言)的整体环境优于现有语言——但通常并非如此。
我认为任何软件工程师完全否定 任何 技术都是愚蠢的。每种工具都有其适用场景,而合格工程师的职责正是判断在特定约束条件下,如何组合这些工具来解决特定问题。
话虽如此,内存安全只是众多决策依据中的一项。对于大量软件项目而言,内存安全根本不是首要考量。易用性、迭代速度、开发者熟悉度、特定库的可用性等因素,往往比内存安全更重要甚至同等重要。
因此,如果你在编写内核、操作系统或关键任务软件,Rust或许值得考虑。否则,其他语言可能更适合你。
[已删除内容]
但每种语言都有这类问题,C语言也不例外。
你大可忽略这些声音。我绝不愿因某些人的无理取闹而错失优质的技术选择。
亲身尝试后厌恶该语言者完全可以弃用。内存问题可通过无需重写数百万行代码的方式解决,从而避免随之而来的混乱、功能退化、人员再培训等问题。
一般而言,程序的目的并非最小化内存安全漏洞数量。在其他条件相同的情况下,内存安全漏洞越少当然越好。但或许你是在用可读性强的漏洞换取不可读的漏洞?Rust的实现很可能比C更复杂(这很合理,毕竟它几乎消除了整类漏洞),而这种复杂性中存在更多非内存安全相关的漏洞空间。
掌握C语言达到特定水平的人数,可能比掌握Rust达到同等水平的人数多出500倍。
若我们拥有能检测C语言内存安全漏洞的分析工具,完全可以将其纳入持续集成管道,或作为代码提交前的钩子机制——在允许向代码库添加代码前进行检测。
这种认为Rust既然没有那些内存安全漏洞,就必然存在大量尚未发现的其他漏洞的想法,让我想起美国人总坚持认为:那些没有他们糟糕枪支安全问题的国家,必定通过其他未被察觉的途径产生了相同后果——比如, 好吧,英国没有美国那么多枪击谋杀案,但肯定有大批英国人被扔酸奶砸死,或是被粗话骂死,只是我们统计时漏掉了?
不,兄弟,事情本可以做得更好,而这正是做得更好的例证。Rust就是更优秀的软件。我们应当从这类案例中汲取经验,而非固执地宣称“不可能做到更好”,并将反证斥为海市蜃楼。
https://en.wikipedia.org/wiki/No_true_Scotsman
你尚未完全理解内存安全的核心问题。编写C或C++代码时,你承诺绝不违反内存安全规则——这正是使用这些语言的基本前提。
关于代码回滚的图表也回应了“难以察觉的缺陷”的论点。
至于分析工具,ASAN正是为此而生。希望无需赘述为何它并非万能解决方案(尽管所有人都该使用它)。
某些功能在Rust中的实现可能比C更简洁。C作为低级语言需要更多底层维护,需通过实现间接表达概念,而Rust则更倾向于声明式表达。
但简单性并非必然。
“懂C”指能读懂代码,与“懂C”指能胜任编写代码是两回事。Rust亦然:非专家完全能看懂Rust算法代码,即使初次接触Rust但精通其他语言者亦然。
某些人时常宣称C比C++或Rust更“简单”、更“不复杂”,因此能产生更易维护或更易理解的代码——在我看来,这种观点 纯属无稽之谈 。
C语言并非简单,而是拙劣。它充斥着大量廉价功能和无法实现的能力,最终导致代码复杂度大幅增加,而非降低。
关键在于,所谓“简单工具=简单工程”的假设根本站不住脚。用挖掘机和钻机盖房子相当直接,但试着用螺丝刀盖房子才叫复杂。没错,祝你好运——你得设计出极其荒谬的流程才能实现。尽管螺丝刀比挖掘机简单得多。
举个简单例子:你想构建一个容器,既能容纳不同类型的数据,又能对它们执行泛型操作。
C++和Rust?轻而易举。模板和泛型搞定。C语言?直到几年前,你的选择只有:1. 复制粘贴(糟糕透顶)或 2. 使用void *(同样糟糕)。
复制粘贴意味着实现方案将分歧,人为增加了维护负担和复杂度。而空指针彻底抛弃了类型安全,迫使你编写远超必要的愚蠢代码,更糟的是它对性能造成灾难性影响!
这只是个例子,但当你深入研究C++或Rust时,类似问题比比皆是。对我而言,这些绝非罕见现象,而是日常编程中随处可见的难题。
匿名函数?又是典型案例。 封装 ?仅仅是避免所有数据都可随意修改?在C语言中根本无法实现。而在C++和Rust中轻而易举,这让程序逻辑变得清晰易懂。
> 面对如此惊人的证据,我实在不明白你为何要完全否定它。
因为它并非万能解药。这种安全性需要付出代价:Rust的学习难度远超C或Zig,同等语义的代码编译时间更是高出一个数量级。它还为Linux内核这类项目增加了大量工具链复杂性。
人们在特定场景中权衡后认为其优势大于弊端,但这些弊端确实客观存在。
Rust的基础学习难度高于C语言,但我不确定在Rust中编写内存安全代码是否比C语言更难。同样,我也不认为学习Rust比掌握C++中同等高级别的编程难度大多少——除非你碰巧陷入了Rust真正棘手的领域。不过多数系统代码并不涉及这些领域。部分代码确实需要处理这些场景,这时就得费力寻找能帮你解决问题的库。C++本身存在大量容易陷入的陷阱,加上多代C++编程风格/面向对象技术的遗留混杂,导致现实中存在大量错综复杂的代码乱局。
(顺带一提,我在本科系统编程课用C教学,初创公司用Python,研究工作则混合使用C/C++/Rust。)
对于处理外部不可信输入的代码,我个人更倾向于使用Rust而非C。在相同开发周期内,我对Rust能避免引入可利用漏洞的信心远高于C。
我最喜欢这样描述:Rust将所有痛苦都前置化。
C和C++是极其微妙的语言。但在遇到某些陷阱前,你能在C/C++中编写大量代码。这使得它们对初学者更友好。
相比之下,Rust如同高墙。编译器对任何错误都会直接拒绝编译。这使得学习过程痛苦得多。但一旦掌握Rust,体验将变得无比顺畅——运行时出现意外的概率大幅降低。
它比C语言更难学吗?入门确实稍难些。但若以同样的质量标准(即减少错误)来衡量,它是否也比编写 规范 的C(++?)更难掌握?
C语言拥有ClangTidy等优质代码检查工具,能指导初级和中级开发者规避错误。诚然,即便使用代码检查工具,C项目通常仍比Rust项目存在更多漏洞,但C语言所需掌握的概念更少。例如在Rust中实现自平衡二叉树,你需要先理解引用计数、
RefCell和所有权语义;而在C语言中,只需知道结构体和指针的概念即可。> 在C语言中,你只需知道结构体和指针是什么。
假设我们有一支专家团队正实时分析代码的每个运行状态。他们通过阅读代码/Valgrind分析/模糊测试等方式——在开发者编写代码的实时过程中进行监控。
每次开发者尝试编译时,团队会迅速投票决定:a)保持沉默让开发者继续,或b)终止编译——因有人发现编译器无法捕获的无效读写等重大错误(但Rust编译器 会 捕获)。
若选择b,专家们会暂停片刻,讨论如何清晰传达隐藏缺陷。随后他们与中级开发者进行简短交流,提出建议,整个流程循环往复。
这种方式是否比单纯学习Rust高效得多?
编辑:补充说明
此假设基于完美学员——编写代码时零失误,甚至零拼写错误。一旦引入错误,C语言侧的处理难度将大幅增加。编译器可能漏检该错误。它可能表现为仅在某些非显性条件下触发的间歇性故障(例如堆内存中越界写入破坏相邻节点数据)。而Rust很可能根本不允许此类错误存在。
同理,在现代繁忙道路上驾驶需要了解车道划分、限速规定、各类路标、交通信号灯、转弯与并线规则等等。若道路上没有这些规范,仅凭方向盘和两个踏板就能驾驶——当然你依然能驾驶甚至高速行驶——但这要求驾驶员投入更多注意力;许多驾驶失误往往事后才被察觉,并导致更严重的事故。
你多久会自己动手实现一个自平衡二叉树?大学时我确实写过几个,但在现实世界中,你几乎总是直接使用现成的库。为这种边缘情况进行优化毫无意义。
况且你手写的C语言实现能保证安全正确吗?你完全没提及任何锁定机制或原子操作,这在多线程环境中会不会突然崩溃?
Rust的 概念 确实更复杂,但实际应用中这意味着C语言更容易让你自掘坟墓。它 简单 ,但这真的是最重要的特质吗?
你究竟有多频繁地在C或Rust中实现自平衡树?
在我看来,这些都是有意义的优缺点,不足以成为彻底否定它的理由。
> 因为它并非万能解药。
它确实看起来像银弹。在软件工程领域,“银弹”一词必然让人联想到弗雷德·布鲁克斯:
《没有银弹——软件工程中的本质与偶然》是图灵奖得主弗雷德·布鲁克斯1986年发表的经典论文。布鲁克斯指出:“无论是技术还是管理方法,都不存在任何单一的发展能单独承诺在十年内使生产力、可靠性或简洁性提升一个数量级[十倍]。”
相较于传统方案,内存安全漏洞减少5000倍的成果不仅是银弹,更是整套银弹武器库。
> 具有等效语义的代码编译时间却增加了十倍
为C和Zig代码编写并运行全面测试以达到Rust免费提供的性能水平所需的时间,比等待Rust编译器的时间多出多个数量级。为何要关注编译明显错误代码所耗费的时间,而不关注生产可靠软件的总耗时?事实证明,像Rust这样的内存安全语言能显著降低后者。
正是这类毫无根据的炒作让人们对Rust望而却步。其安全性和所有权语义并不能免除你编写代码测试的责任。任何代码库中绝大多数测试都针对业务逻辑——或者以Linux内核为例,针对功能实现。
谁说使用Rust就能免除编写业务逻辑测试的责任?我实在看不出你回复的评论中有任何接近这种主张的内容。
这只是某类错误。现有信息不足以做出技术决策。逻辑错误依然存在。目前尚不明确Rust是否更易或更难引发此类问题。现有证据表明面向对象程序更易产生逻辑错误和突发性缺陷。
因此我完全否定这种观点——它欺骗性地将Rust包装成万能替代品。若有人认为这仅针对Rust,请注意:我们对所谓“终极替代语言”的质疑已持续数十年。没有任何证据表明Rust的借用检查器足以解决以往任何语言的根本问题。
对Rust的一个简短批评是:作为号称的系统语言替代品,它却允许过多特性和官方魔法(cargo)混入语言体系。
这就像橘子和苹果的比较,就像回滚率的差异
他们将重写现有代码的新方案(虽非唯一但仍是核心)与数十年的陈旧代码进行比较
在新代码中,他们清楚目标方向
还能运用2000年代初尚不存在的尖端单元测试技术
所以…这些数据嘛…
Rust比C++更合理本就是不争的事实 🙂
Rust实乃工程奇迹,堪称突破性成就。计算机科学领域如此杰作实属罕见。
我对它的起源了解不多,但好奇Rust的特性有多少依赖于近年的计算机科学突破。1990年能否创造出Rust?
编译器速度也相对较慢。在三十多年前的老旧硬件上,Rust是否值得投入使用?
> 编译器速度也相对较慢。在三十多年前的老旧硬件上,Rust是否值得投入使用?
据我所知,Rust编译器速度较慢很大程度上源于LLVM,以及Rust与LLVM的交互方式。Rustc会生成并向LLVM发送数千兆字节的数据,这些数据会被传递给LLVM优化器。若跳过这些步骤(例如运行cargo check),编译器速度可提升一个数量级。
若Rust诞生于90年代,它不会采用LLVM。该语言仍可实现,且编译器速度可能快得多。但它也将错失 llvm 带来的所有优势。它需要编写自己的后端——这将耗费更多精力。编译器在低级优化方面可能不如现在出色,而且很可能无法开箱即用地支持如此多的目标平台。至少在最初阶段是这样。
> 1990年能否创造Rust?
不能。极度简化而言,Rust可被描述为将函数式语言开创的诸多理念带回C++的过程,正如Java将Lisp理念带回C。Rust真正创新之处甚少,本质上只是融合了此前罕见组合的特性。
> 编译器运行也相对缓慢。若在三十多年前的硬件上运行,Rust是否值得投入使用?
Rust编译速度慢的原因与其独特性基本无关。关于这一点已有大量论述,但若再用极度简化的版本来说明:倘若设计者在语言设计和编译器开发阶段就重视编译速度,本可以打造出与Rust高度相似却编译极快的语言。
我认为答案很可能是:Rust在20世纪80至90年代本可实现,但当时这根本不切实际。
Rust以编译器资源消耗大而闻名。在早期个人电脑时代,这种特性是无法被接受的。当时人们需要的是“在我机器上运行良好”且“在你的机器上也该能用”的快速编译器。直接发布就行。
> 1990年能否实现Rust?
我们做到了,它叫OCaml。若当时有远见,本该用它重写所有系统代码。但由于C语言在微基准测试中表现更优,无人问津。
Rust最卓越的特性之一是受Haskell类型类启发的特质系统。它为多数应用场景提供了恰当的抽象,而1990年这些场景通常依赖面向对象和继承实现。类型类基础由Wadler于1988年提出,但某些高级特性(如类型家族)直至2005年才被发明。你提到OCaml,但它仅构成Rust类型系统的一小部分。
因此答案是否定的——1990年人类对编程语言理论的集体认知根本不足以实现这些特性,除非Rust开发者能独立发明这些特性,而非直接借鉴GHC Haskell的实现。
简直疯狂…我从未料到差异会如此巨大。
需注意Rust内存安全漏洞的N=1,因此每行代码平均漏洞数的估计误差相当大。
没错。我估计每千行代码的内存安全漏洞不超过10个,这已是百倍的降幅。
这数字是不是太高了?相当于每千行代码1个内存安全漏洞,实在难以置信。
如果你到处乱用智能指针,这数字倒也没那么离谱。
是每百万行代码。
他们发现Rust代码里存在一个内存安全漏洞,就认定这是整个Rust代码库唯一的漏洞。接着又拿这个数据去对比运行近二十年的C++代码库历史平均值。难道只有我看出这种对比有多偏颇吗?
准确来说,他们是在Rust代码库中发现了一个内存安全漏洞,却拿它去衡量C++代码库中成千上万的内存安全漏洞。两种情况都未将未发现的漏洞纳入统计,因此这并非偏颇。
问题在于这根本不是同类比较。C++代码存在时间更久,其中大量代码是用不具备现代安全特性的旧版C++编写的。我敢肯定他们的代码库里还存在大量new/delete用法。而且我确信他们正在积极排查C++中的内存安全问题,但对Rust可能没那么上心(甚至根本不查)。
你倒是挺有把握的。
即便实际差距扩大百倍,这仍是巨大胜利,核心论点依然成立。
指出这一点很合理,也值得提及。不过我仍希望,尽管可用数据存在差异,这些工程师至少能准确评估该努力的效益,而陈述现有数据才是合乎逻辑的做法。
若需更具可比性的数据,据估算其Rust代码中仅5%位于unsafe代码块内。这意味着仅5%的代码存在内存安全隐患:这已是20倍的提升。考虑到unsafe代码块通常更复杂,我们保守估计提升10倍——但你仍能获得大量Rust的内存安全保障。
关键在于Rust让你明确内存安全隐患的来源:unsafe代码块。C和C++?祝你好运,因为整个代码库都存在隐患。正如他们所言,选择unsafe路径并不意味着放弃所有Rust的安全保障。当然你可以放弃这些保障,但代码审查时会非常明显。总体而言,在Rust中断言“这里没有漏洞”时,你比在C或C++中更有把握。
即便如此,相对而言,若C语言的缺陷未达5万倍之差,至少也应有2000倍之巨。
他们发现了1000个内存安全漏洞,却认定这是运行二十载代码中仅有的漏洞。这般天真何其可笑?
更早的论述特指 Android 的C/C++代码,而非泛指所有C/C++代码:
“我们因其安全性而采用Rust语言,与Android的C/C++代码相比,内存安全漏洞密度降低了1000倍。”
这意味着他们的代码基础相当糟糕。如果当初能多花些时间打磨工程质量,少做些一年后就会被砍掉的功能,他们本可以写出更优质的C/C++代码。
Rust的学习过程实在令人头疼——至少相较于其他更直观的语言而言。但当你与编译器反复较量后,代码终于顺利编译,且深知后续出错概率极低时,那种成就感真是无与伦比。
当然我有点夸张——毕竟我对Rust还算不上资深。
但在用过Ruby、JS/TS和Python之后,这种体验令人耳目一新:只要代码能编译通过,就意味着80-90%的功能已经实现。
而且运行速度也很快。
这是我最爱的特性。Rust将运行时错误转化为编译时错误。
那些与编译器的较量,本质上就是在修复你未曾察觉的运行时漏洞。
Rust是我用过最无缺陷的语言。
我敢打赌,我的生产环境Rust代码错误率比同等规模的JavaScript、Python甚至Java代码低100倍。
Result<T,E>、Option<T>、match语句、if let结构、?运算符以及其他错误处理机制与类型系统的协同运作,使得编写错误代码变得极其困难。该语言的设计目标就是让编写错误变得困难。我认为它取得了巨大成功。
我发现某个时刻,Rust的思维方式悄然占据了我的大脑,我不再与编译器对抗,而是开始与它协作。
我很好奇在哪些场景下编写TS代码时无法获得这种体验?当然我认同Ruby、JS和Python的情况。
TS中一个主要的错误来源是结构共享。比如当某个复杂对象需要被多处访问时,最直观的高效共享方式就是传递引用。但这很危险——开发者很容易忘记对象已被共享,在某处修改时忽略对代码其他部分的影响。
我在 TypeScript 中犯过这种错误的次数多到不愿承认。这会引发难以追踪的顽固缺陷。避免此类错误的常规方案是实现深度不可变性,或采用克隆替代共享。但这两种方案在语言层面支持不足,且从性能角度看代价高昂——我不想在非必要时承担这种成本。
TypeScript相当优秀。但TS程序通过类型检查却仍含漏洞的情况非常普遍。根据我的经验,Rust编译器漏过的漏洞要少得多。
感谢分享,这很有道理。我感觉自己被训练得在每种语言中都过度偏好不可变性,有时反而会忽略这些问题。
同感。我通常围绕管道和生命周期设计代码。生命周期越长的元素越靠近程序开头。若需修改,我会确保实际修改集中在单一位置,以便区分读写操作。其他情况则采用克隆后更新的方式。虽然效率可能不高且需监控内存使用,但逻辑实现简单得多。
虽非父级评论,但需强调:若系统边界类型正确,TS通常安全可靠;反之则风险极高。我遇过的最具破坏性漏洞,往往源于HTTP调用类型与实际数据结构不匹配。
此外,许多内置函数缺乏足够的类型安全保障,例如Object.entries()
我不懂Rust,但真心好奇:它如何解决这个问题?
当调用REST API(或SQL查询)时,它如何确保返回数据符合类型规范?
TypeScript允许你解析JSON,将其转换为目标类型就完事(这会隐藏正确性缺陷,除非使用运行时对象结构验证,详见兄弟评论)。Rust是否强制执行这种验证?
它会在运行时验证对象结构,类似于在TypeScript中使用Zod这类库的做法。关键区别在于:Rust会让你不敢不验证数据,而TypeScript即使在严格模式下也乐意让你放手一搏——结果往往是自食其果。
明白了,这是个不错的默认安全机制,而TS显然不具备这种特性。
最恶劣的罪魁是toString——它默认在不同对象间存在类型差异,且无处不在。
此处“安全”具体指什么?
若能在系统边界处正确输入,TS将极近于实现代码的正式验证。虽无法捕获所有漏洞,但即便对数据进行粗略分类也大有裨益。若确认输入为非空字符串,它将警示所有非字符串用法。虽无法区分名称或邮箱类型,但能发现有人试图进行除零运算已属难得。
这是TS机制固有的局限,但可通过库验证反序列化数据结构显著改善。zod是典型方案,或可采用protobufs。本质上这是所有编程语言的通病。但将基础“结构体”类型的底层设计为哈希表会导致更多错误,因为它会接受任意键值组合。
TypeScript甚至不支持“无符号整数”这类概念。它并非对类型安全的严肃尝试,其主要卖点是“比原始JavaScript更优”——这实在算不上什么成就。
需注意谷歌至今仍未正式支持在Android用户空间使用Rust。
尽管博客中列举诸多优点,NDK仅支持C和C++工具链,Android Studio亦然。若有人想使用Rust,相关工作仍需社区自行完成。
但没人阻止你用NDK编译Rust。Android的ABI与其他平台并无二致,系统只关注是否遵循规范——无论你用Rust还是其他语言构建.so文件。
有些人宁愿等待官方支持,也不愿为大公司填补技术空白。
所谓官方支持,是指期待NDK内置Rust工具链?还是希望NDK提供Rust绑定库?
后者尚可理解,但我仍质疑是否该为Rust依赖项开特例——毕竟绑定库本可托管在crates.io。
或者他们应该提供调用现有Rust工具链的脚本。
我期待Rust在此处获得文档支持:
https://developer.android.com/ndk
我期待整个Rust构建流程成为Android Studio的组成部分,包括Java、Kotlin和Rust之间的混合语言调试。
我期待所有NDK API都提供Rust实现库。
我期待Android开发者论坛同样关注支持Rust开发者。
以及任何我遗漏的、但Java/Kotlin/C/C++已具备的支持功能。
Android NDK对C/C++的支持也仅够用,除非你接受1990年代的工具标准。整个项目感觉像是由两个被锁在谷歌地下室的家伙维护的。除非Android工具链发生重大战略变革,否则我怀疑他们根本无力处理NDK的Rust支持。
若想在Android上实现更安全、高完整性且经形式验证的代码,别忘了这份针对SPARK的期望清单[1]!
[1] https://www.adacore.com/about-spark
我认为我们确实可以放弃这个想法了。
这颗炸弹将在2026年终结C++。
尽情辩解Rust“同样”不安全吧——用对工具C++也能做到,高手还能做得更好云云。
Rust有其他优势。我认为Cargo比CMake优秀,语法更优雅,依赖与模块管理更出色。
编写“安全”代码可能令人烦躁,但一旦达到特定标准,我就能对编写的并发应用充满信心。
我希望能用Rust开发安卓应用。实在不喜欢Android Studio的Java开发环境。
> 我认为cargo优于cmake
我猜谷歌自家代码大多不采用这两者,而是使用专属构建系统(我认为不同语言间该系统是通用的)。
但若非谷歌,我完全赞同这种观点。
谷歌3使用Blaze——这是内部版的Bazel。它非常出色。我也喜欢Facebook的BUCK,本质上属于同类工具。
若我加入其他公司,会积极推广上述任一工具。
Android在Rust开发中采用什么方案?
Android使用Soong:https://source.android.com/docs/setup/build
专有代码使用内部版本的Bazel:https://bazel.build/
没错,据我所知谷歌有个万能编译器,能编译任何东西,还带一万个命令行选项。
它就是https://en.wikipedia.org/wiki/Bazel_(软件)
恕我无法欣赏Rust语法。
对我而言理想的语法是C/Go,这点必须说清楚。
但我认同Cargo引入的工具链确实令人耳目一新——在这个被庞大Makefile和仓库内直接复制库文件主导的世界里(我知道还有Conan、vcpkg等工具)…
> 抱歉,我实在无法欣赏Rust的语法。对我而言理想的语法是C/Go,这点必须说清楚。
若此言显得轻蔑还请见谅,但我实在难以认真对待这类语法抱怨。学习新语法其实很简单——如果你熟悉C和Go,可能不到一小时就能掌握Rust全部语法。Rust语法唯一令人惊讶的,是那些奇特的match表达式变体。
Rust真正令人意外的是 语义 设计。比如生命周期的运作机制(以及何时需要显式指定)。这些才是真正困难的部分。但纠结if语句不需要括号这种事——老兄,这根本不值一提。若你打算在职业生涯中拒绝学习新事物,软件行业不适合你。
大约15年前我接触Objective C时,朋友们只知道它有“那种奇怪的语法”。适应起来却毫不费力——输入[]字符根本不算难事。
我认为Rust在冗长与简洁之间存在种奇特的矛盾。
一旦涉及生命周期管理,冗余度就会显著攀升。而指针操作最终会引入多层概念,这是C++/Go/Java中未必会遇到的。
然而默认启用类型推断时,Rust常能展现惊人的简洁性。集合操作的精炼程度堪称语言之最。
我认为这正是语法争议的根源所在。
C++通过移动语义、简写语法、自动转换,以及将生命周期问题推迟到运行时处理,掩盖了大量复杂性。而Go/Java/Python这类语言则将所有内容推入堆内存,试图避免暴露底层内存语义。
我完全理解为何有人不喜欢Rust。个人觉得它很不错,不过我的思维模式已被Perl和PHP彻底扭曲了。
我直言不讳地讨厌Python语法,但不会因此拒绝编写Python代码。若有必要使用Python,我顶多抱怨几句还是会写。
关键不在于学习语法,而在于能否忍受阅读体验。我个人觉得Rust语法令人反感,除非Rust是唯一选择,否则我会选其他语言。所幸现在和将来它都不会是唯一选项。
这三种语言的语法即使和Python或Ruby相比也相当相似,更不用说ML或Haskell了。这抱怨似乎毫无根据。
我的意思是,若你指的是这三种语言,Rust与C或Go属于完全不同的类别。
Rust的语法大量源自ML(好吧,并非直接继承,可能通过OCaml间接影响)
模式匹配、解构、随处可见的表达式等特性,C/Go根本闻所未闻。
不必喜欢它。它不是脑残语言,能完成任务就行。
我并不反对,这正是我如此钟爱Swift的原因之一。Rust在我看来太像C++了。
但若我在C++环境工作且有C++或Rust可选,基于此我会选Rust。
赞同!忘了提Swift。多么优雅的语言啊。
Go真是绝妙的语言。若你的代码库不介意垃圾回收且不依赖外部库,所有人都该关注Go。出色的多线程支持、内存安全机制、完善的错误处理,加上对C++/Java等背景开发者友好的语法。
Go的错误处理机制堪称我见过最糟糕的,甚至比多数异常实现更差。其类型系统愚蠢至极,依然极易引发编程错误。Go的内存安全仅是表面现象,你完全可能在Go中遭遇段错误——我指的是真正的段错误,而非空指针解引用。
> 优秀的错误处理
Go 完全搞错了!它用元组而非枚举来表示潜在错误。这意味着你可能忘记检查错误,直接使用函数返回的无效值(空值?)。
另一方面,Rust 通过 Either 枚举强制处理错误。或者你可以使用 ? 运算符将错误传递给调用函数,这会反映在外围函数的类型声明中。
没错,Rust的方案胜出。几乎所有新兴语言都在采用Result风格的错误处理,现有语言也纷纷引入该机制。
这是函数式编程的产物,对我而言,在非函数式编程的轨道式风格之外,实在难以想象还有何种更优的错误处理方式。它就是如此卓越。
挑剔小毛病:这根本不是元组。Go语言没有元组,它只有特殊情况下的“多重返回值”,这意味着通常无法直接将函数调用的返回值(包括错误值)塞进任何数据结构来聚合它们。由于Go中不存在元组,你必须先定义结构体才能实现聚合。
Go如何实现内存安全?内存安全不等于“不会内存泄漏”。
计算错误的指针偏移绝对可能发生。解引用空指针绝对可能发生。破坏所有权导致多线程践踏结构体绝对可能发生。错误解释内存绝对可能发生。
我承认垃圾回收机制能避免(多数情况下的)未分配缓冲区利用(UAF)。数组越界索引确实不可能发生。但Go的内存安全性远不及Rust的级别。
> 完全可能破坏所有权机制,导致多线程同时篡改结构体。
这正是Go语言在内存安全方面存在缺陷的症结所在。
> 计算错误的指针偏移量完全可能发生。
在Go语言中?若不使用
unsafe包(此时你已明确放弃安全机制)?如何实现?Go语言根本不存在指针偏移量。> 绝对可能发生对空指针的解引用操作。
没错,但这是 Go 语言中定义的安全行为,相当于 Rust 中解包 None 选项时的行为。它会可靠地引发崩溃。这与 C 语言不同——C 语言中这种行为未定义,可能导致崩溃,也可能只是随机损坏内存,甚至让优化器让代码执行更奇怪的操作。
这(实际上是缺乏非空类型)是Go无法像Rust那样产出可靠软件的诸多原因之一,但这并非内存安全问题。
> 内存绝对可能被错误解释。
再次强调,没有unsafe包的情况下?如何实现?
利用数据竞争:https://www.ralfj.de/blog/2025/07/24/memory-safety.html
Rust并不认为内存泄漏属于不安全行为。
Zig本质上是去掉GC的Go语言。若GC不符合你的使用场景,Zig堪称绝佳选择。可惜它距离稳定到能用于编写严肃应用大概还需5到10年(Bun和Ghostty是例外,但它们背后有强大团队持续更新以保持与Zig同步)。
> 若您的代码库不介意垃圾回收且不依赖外部库,Go语言值得认真考虑
毕竟在这种前提下,几乎所有语言都是不错的选择。
> 卓越的多线程能力
直到你遭遇无数陷阱。好在这些陷阱通常不会导致内存安全问题,只会产生垃圾数据。
Go语言中的数据竞争模式
https://www.uber.com/blog/data-race-patterns-in-go/
>提及Go语言
>出色的错误处理
为何人们如此自信地犯错?
起初我也讨厌它,但现在觉得还行。
>我觉得 cargo 比 cmake 更好
这简直是轻描淡写。我实在想不出哪个构建系统比 CMake 更让人想寻找替代方案。
纯粹出于对 CMake 的厌恶,尝试开发自有 C/C++ 构建系统和/或包管理器的开发者多到难以统计。
公平地说,无论是对这些开发者还是CMake本身,这确实是个棘手难题。真正了解CMake的人都会希望自己从未接触过它——这样就能让别人来维护了。
你似乎忘了autotools的存在。CMake虽然丑陋,但我宁愿用它也不要autotools。
作为终端用户:至少autotools能通过./configure –help清晰展示可用配置选项。CMake虽有-LAH参数,但体验依然…糟糕透顶。
不过它至少懂得如何使用ninja。
问题在于交叉编译时autotools总出错——选项明明存在却总出问题,且难以修复。CMake至少能搞定这点。需知我常做交叉编译,这点对我至关重要。若只做常规操作,autotools或许可行——但make同样能胜任。
确实;总体而言我发现CMake规则比autotools和Makefile更易于阅读和修改。Makefile里编译规则有18种写法,每次都要翻遍文件才能搞清楚某个Makefile如何定义特定C文件的编译规则。CMake则更具抽象性。我能清晰看到所有高阶构建目标及其生成逻辑,同时还能独立修改CMake编译C代码的底层构建系统。这种设计更符合逻辑。
但我宁可选择Cargo也不要这些东西。Cargo意味着我完全不必考虑编译器标志。
我不喜欢Rust,但比起“Android Studio的Java/Kotlin那套”,我绝对更倾向于它。
Cargo简直糟透了。它或许比cmake强些,但仍是Rust最糟糕的部分。它完全不透明,还混杂着大量不同功能。
Rust软件分发如此痛苦,主要归咎于Cargo的设计。要分发不会给下游用户带来头疼的软件,几乎是不可能的。
> Cargo简直糟透了。它或许比cmake强些,但仍是Rust最糟糕的部分。它完全不透明,还混杂着大量不同功能。
“简直糟透了”这说法未免太夸张——你也是这么想的吧?在你看来,哪套工具链不是糟透了?
Cargo 当然远非完美——确实存在一些普遍诟病的问题和诸多尖锐缺陷(其中多数在 GitHub 上已有多年未解决的 issue),但… “糟糕透顶” 终究是夸张之辞。
这听起来很像某些Linux发行版遇到的困境:它们期望能为每个库提供单一预编译版本,让应用在运行时动态加载。
但这根本 不符合Rust的设计理念 :这是在强行将方枘塞入圆凿,你遇到困难不能怪Cargo。
能详细说明吗?
相较于CMake之流,我用Cargo时感到如沐春风。它如何阻碍下游项目使用?对我而言很简单——直接共享源代码仓库即可。
你是想分发预编译代码之类的东西吗?这种场景确实更复杂——Cargo确实不支持这种用例。
你认为该如何改进 Cargo?
呃…怎么说?
对任何开源项目而言,Cargo都是福音。所有依赖打包完毕,只需执行
cargo build。别敢说 CMake 或 autotools更好,那不过是斯德哥尔摩综合症作祟——你只是习惯了它们罢了。说真的,CMakeLists.txt 和 Cargo.toml 根本没法比!前者是充满晦涩条件和冗余代码的命令式文件,后者却是声明式的包清单啊?
不过软件分发确实有个痛点,尤其困扰分发包维护者——整个生态都围绕 C 语言模型和动态链接构建。这甚至不能归咎于Cargo,毕竟Rust的ABI不稳定导致动态链接大多时候行不通。另一个棘手问题是泛型处理——必须进行单态化才能与动态链接兼容(除非使用
Box<dyn>);C++其实存在相同困境,这正是为何涌现大量“仅头文件”库的原因。能否详细说明你的论点?因为在 uv 这样的项目中情况恰恰相反。人们热衷于单文件可执行程序——只需为特定平台编译即可让用户直接下载。uv 项目显然乐在其中,这证明并非“不可能”。或许他们采用了我不了解的特殊方案?或者你的使用场景不同?那么你所处的具体情境中究竟存在什么不可行之处?
这不也是npm的模式吗?
我个人对这些派系之争毫无兴趣。只庆幸开发者能获得更多优质选择来构建更安全的系统工具。
我遇到的所谓“派系之争”论调,多出自死硬C++拥趸之口——他们骨子里排斥任何其他编程语言。Rust终究只是门语言,如同所有编程语言都有其优缺点。其中某些优势确实极具吸引力。
个人而言,看到C与C++双寡头垄断局面开始出现真正竞争者,我反而如释重负。此前所有新兴语言都采用垃圾回收机制,却以糟糕的运行时性能为代价换取炫酷特性(例如Java、C#、Ruby、Python、Lua、Go等)。
Rust是门优秀的语言。我个人迫不及待想看到它的继任者——某些特性必然存在更优的实现方式,期待有才华横溢的开发者能开创先河。
这观点令人意外。Rust的营销策略完全建立在——正如本文所述——将其内存安全性与C/C++对比,并宣称C语言糟糕的基础上!
即便在其自定义的“内存安全性”定义中(谷歌搜索首位结果),他们也选择批判C语言而非给出合理定义:
https://doc.rust-lang.org/nomicon/meet-safe-and-unsafe.html
我认为在实质上与直接竞争对手形成鲜明对比,还不足以构成一场“敌对战争”
> 即便在其自称的“内存安全”定义中——该定义在谷歌搜索中排名首位——他们也只是批评C语言而非给出合理定义:
我不确定该页面是否真的旨在定义“内存安全”?它(及后续页面)似乎更侧重于介绍安全/不安全的Rust语言及其界限。
该内容出自《Rustinomicon》,其中声明:
> 与《Rust编程语言》不同,我们将假设读者具备相当的先验知识。尤其需要熟悉基础系统编程和Rust语言。
因此该书未给出内存安全定义实属意料之中。
若需更精确的定义,建议查阅Rust参考文档(例如[0])或相关领域资料。
[0]: https://doc.rust-lang.org/reference/unsafety.html
毕竟我们确知Rust存在安全隐患——专门记录其各种安全漏洞的bug追踪器就是明证。我最欣赏的是:在100%安全的Rust中,无论实际生命周期多短,都能将其强制转换为静态类型。
(这并不意味着它不如C++)
我认为援引cve-rs[1]中那些众所周知的案例纯属恶意之举。当然,若你极力想编写不安全的代码,终究能做到。生命周期变体规则下游类型系统中的边缘案例,在实际编写安全软件时根本无关紧要。这个追踪器之所以有趣,在于它在探测编译器的边界,但它完全不能证明“Rust不安全”。
[1] https://github.com/Speykious/cve-rs
这并非真正的“强制转换”。编译器禁止此操作是因为它会导致引用存活周期超过其所有者。自由“转换”仅适用于本质上静态的数据类型,此时实际发生的是强制转换。其他情况必须使用
std::mem::transmute或Box::leak等机制。这里有一个在完全合法安全的Rust中发生的漂亮段错误https://play.rust-lang.org/?version=stable&mode=debug&editio…
我称之为强制转换,但技术上可能并非如此,或许该另起新名?无需使用transmute或leak。该问题至今已有十年历史https://github.com/rust-lang/rust/issues/25860
没错,这是编译器现存的正确性漏洞。不过你不会无意间自己写出这种代码。
若判定标准是“蓄意恶意代码导致段错误”,等修复后再联系我
编辑:这都算是宽容的表述了;Rust官方视其为编译器缺陷,理应修复。
Rust世界里最让我心头暖意融融的,正是这个不安全的漏洞追踪器。
那些本应安全的构造被各种手段强行篡改,最终产出意料之外的垃圾——而人们却因这些问题被当作漏洞处理而投入精力修复。
这简直是“我们为所有程序保障安全与正确性”的最佳广告。
https://github.com/rust-lang/rust/issues?q=state%3Aopen%20la…
我希望能看到Rust与C++的开发时间对比,但总体而言我基本认同。若使用具备完整特性的现代C++,Rust通常是更优选择。
话虽如此,即便在C语言中实现部分指针语义也相当容易,其功能可覆盖Rust的95%。
> 话虽如此,即便在C语言中实现部分指针语义,也能完成Rust功能的95%。
设计具备内存安全指针的语言并不困难。但要设计出既具备内存安全指针,又不依赖沙箱、虚拟机或其他会产生运行时开销的动态检查机制——这些机制从根本上就使语言丧失了在此领域竞争的资格——这绝非易事。
这些数据基本印证了显而易见的现实,但回滚率竟有4倍之差,天啊。
我们都知道Rust更安全,但1000倍的差距着实让我吃惊。
在我看来,这并非表明Rust本身不够安全,而是说明缺陷普遍性之高超乎想象。
4倍的差距同样令人震撼。
能看到如此庞大复杂代码库的数据实属难得。
多数代码库的问题在于,开发者从始至终都未考虑建立验收测试体系。
正确的做法是:在编写代码之前,就设计一个模块化的验收系统,根据当前工作内容运行完整的测试套件或子集。
这本质上就是软件的契约。在模块层面,它意味着将契约范围缩小到各个子系统。内存和CPU运行时限制等因素都包含在内。
若具备此机制,你便能用测试替代Rust编译器的功能。内存泄漏得以捕获。更可贵的是,开发周期中的变更(如性能退化)也能在同一系统中同步验证。
我不确定是否无人想到这点。我们时间有限。通常稳健的方法能带来稳健的性能。即便偶尔出现性能相关缺陷,修复效率仍远高于从零设计严谨流程并推动全员执行。
消除编译器引发的各类缺陷意义重大——既免去额外验收系统的设计,又无需强迫整个团队遵守纪律。若能实现无缝衔接,这将是极具吸引力的创新产品。
> 理想状态是:在编写代码前就设计好模块化验收系统,运行完整测试套件……
有时确实如此,但取决于具体工作内容。
编程的趣味挑战在于,这个过程会让你在各个层面认识到自己的错误。语法可能有误,实现的算法可能有误。模块设计思路可能存在偏差。甚至可能完全在解决错误的问题!比如耗费数周为游戏添加功能,结果反而降低了游戏趣味性!哎呀!
测试在抽象层面上将你对代码功能的预期转化为形式化规范。但若这些预期被证明是错误的,当你尝试重构时,测试本身就会成为阻力。在探索问题空间的初期,你需要尽可能简化重构流程。
当然,某些程序不受此困扰。若你正在实现C编译器或grep替代工具,那些明确的验收测试在项目生命周期中几乎不会改变。
但并非所有问题都具备如此明确的边界。有时你是在创造编程语言,开发游戏,或是设计用户界面。在我看来,那些从一开始就被完全约束的问题往往最缺乏探索价值——何来发现的乐趣?
我只想说,谷歌是少数能近似实现这种状态的地方。我们拥有(相当完善的)测试套件。可仅运行受影响的测试。可通过仪器化(*SAN)进行测试,可在不同内存和CPU限制下运行,还可并行运行1000次以消除不稳定性。
总之谷歌具备所有这些条件,却仍能实现这种改进。
在代码之前编写测试仅在接口已完全定义且规范明确时才真正有效。虽然确实存在这种情况,但根据我的经验,通常并非如此。
若新代码使用Rust而C++变更仅涉及旧代码,这完全可以用旧代码变更风险更高来解释。
有趣的是,另位评论者持相反观点:Rust更可能用于移植现有功能,因其已有完善测试框架而更易实现。
若你实际编写过大量Rust和C++代码,这些统计数据无需辩解。在我看来,Rust代码更易正确编写完全合乎预期。
作为在科技行业工作数十年但非软件开发背景的相对新手程序员:我不同意“必须大量编写Rust和C++代码才能验证这些统计结论”的观点。事实上,尽管Rust初期学习曲线陡峭,我认为任何尝试用Rust或C++实现稍复杂功能的初级开发者都能体会到其优势。
至少,IDE集成能实时反馈代码问题及其成因,这对初学者而言能极大提升效率。
我完全赞同你的观点。
我评论的逻辑并非要求必须编写大量代码才能产生这种预期,而是认为若未产生这种预期,则暗示你尚未积累足够经验。希望表达清楚。
关于第二点,我认为C++的IDE集成与Rust类似。只是Rust的错误提示和工具链无论搭配何种IDE都强上千万倍。
哦,开发者资历越浅,受益越快。这在任何强调正确性的语言中都很常见,但C++和Rust的对比根本不公平——C++本身就是门极其难用的语言。
至于他们能否真正“领悟”,那就另当别论了。
我认为Swift和ObjC的对比也适用同样道理。
是时候终结C时代了。
苹果本该现代化Objective-C而非让Swift成为通用语言。迭代速度与灵活性(本可实现媲美Web栈的生产力特性)已永远消失。
Swift并发机制如同轮胎大火,连其异步算法团队都无法完全正确使用,而类型化抛出等实用特性则半途而废。Swift持续恶化的开发成本,至少部分导致了开发工具的窘境——即便市值4万亿美元的公司,在自家IDE中都无法实现可靠的SwiftUI预览功能。可变参数泛型(看似纯粹的编译器特性)若稍有不慎便会在运行时崩溃。作为结构化并发体系核心灯塔的Actor模型,因调用顺序无法保证而彻底失效。如今虽强制执行严格并发检查,但编译器愚钝到无法推断常见的有效发送模式;而他们让这堆垃圾在真实代码库中运行的解决方案竟是—— 直接引入默认行为——默认让模块内_所有内容_都在主线程运行!
</吐槽结束>Swift存在太多问题,老实说与其修复Swift,不如直接迁移到Rust更好。说真的。编译器解决类型推导时竟如此容易陷入指数级时间消耗,导致它经常彻底崩溃,逼得你重写代码才能勉强运行——这对于你所说的市值4万亿美元的公司而言简直可耻。这暴露了Swift的深层问题。
我完全认同你的观点。
只要C调用约定继续主宰操作系统和FFI领域,这种状况就会持续拖沓。希望终有一日能解决这个问题——无论用什么语言,都得依赖C来调用SomeLib,实在令人恼火。
我认为他们是在比较Rust的新代码与C++的新代码。
有趣的是,尽管代码规模存在差异,回滚率却基本保持稳定。
这并未控制混杂因素。
例如:人们更倾向于重写那些理解透彻的代码,而理解透彻的代码通常审阅周期更短、回滚率更低。
而那些只有少数资深开发者能理解的复杂代码——它们拥有海量测试覆盖率、由测试和经验强制执行的长尾需求,以及极端严谨的文化?这类代码往往经历更长的审阅周期,更高的回滚率,且极少被重写。
首张图表确实提供了有力证据,表明实际情况并非如此。若“简单”代码确实被频繁重写,那么随着难以处理的模块持续保留在C语言中并引发新内存漏洞,“新增内存不安全代码比例”与“内存安全漏洞比例”理应呈现不同增长趋势。
个人经验同样佐证了这一观点——我确信人们更倾向于用新语言重写引发问题的代码。
当然这并非盲态随机对照试验,而是观察性数据。虽无法完全排除混杂因素的影响,但该现象真实存在的可能性远高于虚假结论。
我预计多数C/C++项目中的内存安全漏洞正因实践改进而减少
这与谷歌关于自身代码的报告相矛盾——他们指出多数漏洞存在于新代码中
我认为“C++代码漏洞总体呈下降趋势”与“多数漏洞存在于新代码”这两种说法并不矛盾。
你会大失所望。
本文主要讨论新代码。
通过按S/M/L规模分组比较,这种影响有所缓解。
按行删除量分组分析(例如标记重写部分)确实很有意义 (并标注其规模)
> 那种只有少数资深开发者能理解的复杂混乱代码?它拥有庞大的测试覆盖率,由测试和经验强制执行的长尾需求,以及极端严谨的文化?更长的代码审查周期、更频繁的回滚操作,以及更低的重写概率。
我认为这恰恰是最可能被重写的部分——高测试覆盖率正是重写的强大推手,而“仅有少数资深老手能理解”的项目本质上是巨大的组织隐患。
话虽如此,虽然我相当确信Rust能带来巨大效益,但我同意你的观点——这些数据不应被视为严谨的科学证明。它更像是Rust优秀的又一个轶事证据。
> 它更像是Rust优秀的又一个轶事证据。
但这很可能沦为最糟糕的科学:
– 某群体认同Rust优秀,这属于主观信念
– 该群体迫切需要寻找佐证其信念的论据
– 他们以这种方式进行“科学”研究
而我们其他人却认为这个数据点值得信赖,实际上它不过是为传达观点而精心挑选的数据。
> 而我们其他人却认为这个数据点值得信赖,实际上它不过是为传达观点而精心挑选的数据。
将谷歌的做法称为“科学”并指责其选择性采样实属不公。这属于观测数据范畴,但你对他们的方法论有异议吗?还是仅凭(主观?)负面印象?
科学研究中,人们会竭力控制混杂因素。
这并非如此。
大量观察性科学研究与本文采用相似方法,根本无法控制混杂因素——原因恰恰在于现有数据中根本无法实现有效控制。
作为亲历者,我目睹过有人历经博士深造投身科研,这种对当今科学实践的认知虽浪漫却错误,实属可悲的普遍现象。
刻意为之往往意味着进行不道德实验,这绝对是科学界所不齿的。
许多实验根本不可能完全排除所有外部因素。但这并不意味着实验结果就必然无效。
我用Rust做游戏开发(不用Bevy)。正因其稳定性和吞吐量,我几乎不会考虑其他语言。
如今将Rust应用于基础关键代码已非艰难攀登,其优势显而易见。或许只是场残余的宗教战争?
无论如何,我很高兴看到越来越多证据和案例研究证明“用Rust重写”绝非空谈。
但这里讨论的是“用Rust编写新代码”,而非重写。
呃,我认为并非非此即彼。谷歌已着手将部分问题组件重写为Rust。例如:
Binder内核驱动程序:https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/lin…
媒体编解码器:https://www.androidauthority.com/android-16-in-process-softw…
是的,还有一个替代FreeType的方案https://github.com/googlefonts/fontations
不过我觉得他们主要是避免无谓的重写。被重写的部分往往存在历史安全漏洞或其他问题,即使不用Rust实现也需要重构。
微软也在进行类似工作:
> 用Rust重写SymCrypt以现代化微软加密库
https://www.microsoft.com/en-us/research/blog/rewriting-symc…
谷歌用Rust重写了Android的蓝牙协议栈。
另提及:
没错,但宏观层面仍坚持用Rust“重写”安卓子系统的策略。只是进度缓慢。
去看看Phoronix任何涉及Rust的文章评论区,你会发现80%都是Rust黑在重复老生常谈:C++和汇编也能实现同等安全特性!Rust照样能写出漏洞!我认识个学Rust的家伙不小心删了主目录,大家还是用C吧!
全是胡说八道,要不是太无知简直令人发笑。
>全是胡说八道,
这些说法哪里不对?
从“你能用C语言编写Rust编译器,从而用更优秀的语言编程”这个角度看,没错,这些说法都正确。
1. 你根本 不懂 自己在做什么——谁都会犯错。
2. 在Rust中依然可能编写出错误代码,但关键在于出错概率会大幅降低。
是啊,但错误率降低1000倍????我指的是安卓项目背后的团队至少属于顶尖工程师行列,但天啊,如果他们都能取得如此进步,普通开发者能从中获益多少就不得而知了
并非整体错误减少千倍,而是Rust专门设计消除的特定类型错误减少千倍。
开发者信心和入职时间也有所提升,但幅度不及前者显著。
Rust在Android场景下有其合理性——毕竟内核和软件都是谷歌开发的。就像2010年代Java在后端服务领域有其价值,尽管当时它存在缺陷,直到Node和Python实现重大性能飞跃且计算成本降低。
但这只是Rust适用性的极端特例。反对Rust的人(如我)并非否定其价值,只是反对将其应用于所有场景。
当看到“[…]应重写为Rust以实现内存安全”这类论调时,足见人们根本不理解内存安全本质。这种源于计算机科学教育缺失的愚昧观念,认为任何代码都可能随机出现内存安全问题。
Rust的弊端在于其所有权语义常使编写过程繁琐,从而拖慢开发进度。加之底层机制仍在演进,该语言尚未成熟。在多数场景中——尤其是网络延迟占主导、CPU周期耗费在等待响应的休眠状态时——根本无需用原生编译代码替代Python或Node.js这类更灵活、开发更高效的语言。
因此在多数场景下,当你能更快编写出完全内存安全的代码时,Rust并不适用。
不过在C和C++中这确实成立。若有人能展示从未出现内存安全问题的非trivial项目,那必然是质量把控松懈的项目。即便像SQLite这样由顶尖程序员打造、对质量近乎痴迷的项目,也未能达到这个标准。
> 存在这样一种愚蠢的观念——源于缺乏正规计算机科学教育,认为任何代码都可能随机出现内存安全问题。
笑死。这种观点放在你评论的文章背景下简直荒谬。认知失调到极致。
> Rust的缺点在于其所有权语义常使代码编写繁琐
技术问题
我以为这篇文章是讲用Rust做移动开发,比如Android上的Tauri框架。
Rust的特点是所有成本都得 upfront 支付,回报才迟迟到来。首先要付出学习成本——这本身就不容易。然后每次编译代码都要付出代价,这会严重阻碍开发进度。学习阶段常会遇到这种时刻:因继续推进代价过高,不得不彻底重构方案。此时许多人会喊“Rust太难了!”而放弃。
但若能坚持下去——就像谷歌那样——回报在于:相较其他语言,你不必持续承担这些成本,反而能在长期收获回报。
首先,Rust具备类似Haskell的特性:只要逻辑正确,编译通过的代码通常运行无虞。这使得测试效率倍增——因为编译器早已处理了测试阶段探索的所有边界情况。
这种特性同样体现在更轻松的重构上——你可以在代码库中进行大范围改动,并确信能完整复原所有功能。
更重要的是程序本身 运行迅捷 。有多少次uv被提及时,人们的第一反应是“哇,它太快了!”。速度本身就是一种特性,用户每次运行代码都能从中获益。
其他语言很难同时兼顾这些特性。通常它们要么像Rust一样快速但难以编写,却缺乏安全保障;要么像Rust一样安全,却牺牲了速度。正因如此,Rust占据了其他语言难以企及的黄金位置。
没错,我在工作中使用Rust,它真是个福音。
它能轻松将证明/不变量嵌入类型系统,同时仍能掌控内存模型。
Rust的核心优势在于其社区生态,拥有海量优质库
未来可能取代/演进Rust的语言,将是基于双层类型理论的语言——既能实现零成本抽象,又能支持完整的依赖类型理论
我很高兴你这么说,因为这正是我研究的方向 ^_^
https://andraskovacs.github.io/pdfs/2ltt_icfp24.pdf
我认为这类语言才是终极实用编程语言
我认为Rust比C++更容易上手
我同意你的观点——前提是你没学过C++。我实际在教只懂Java的学生学习C++和Rust,他们掌握Rust的速度快得多。真正吃力的是那些带着C++惯用法接触Rust的人。关键在于工具链,尤其是Cargo作为Rust全栈解决方案的地位。虽然有人抱怨Cargo功能过于臃肿,但对多数学习者而言这反而是优势。
>若代码能编译通过,通常运行也毫无问题。
这与Java的论调如出一辙——它具备内存安全特性,在所有权管理上相当严格。
但事实是,Rust仅解决了内存安全的单一问题:双重释放。若你连这点都无法理解,便不该妄谈内存安全。
若从未遭遇此类问题,你永远无法体会其价值。
>更何况程序本身运行速度快。有多少次讨论到uv时,人们的第一反应都是“哇,它太快了!”
这充其量只是种感觉。速度差异确实存在,但在整个工作流中存在更耗时的环节(比如从互联网抓取数据)时,这种差异微乎其微。
简而言之,Rust只适用于特定场景。Android恰好符合条件:a) 需要原生代码性能;b) 多团队协作开发众多服务;c) 生态相对封闭且能掌控底层细节。正因如此,在缺乏严格检查的数据传递过程中,双重释放才是真正的威胁。
> 事实上,Rust仅解决了内存安全的一个方面——双重释放。若不理解其原理,便不该妄谈内存安全。
Rust如何应对释放后使用问题?
> Chromium:PNG、JSON及网络字体的解析器已替换为Rust实现的内存安全版本,使Chromium工程师更易处理网络数据
我对此感到意外,难道Wuffs[1](同样由谷歌开发)不是更适合这个特定用例吗?(它具备编译时空间内存安全,而Rust仅有编译时时间安全,运行时需通过边界检查实现空间安全)。
显然在通用系统编程领域,Rust是毋庸置疑的首选,我很高兴看到谷歌推进Android的Rust化进程。
[1]: https://github.com/google/wuffs
这并不令人意外,仅从采用门槛来看:“Wuffs程序需要开发者花费更长时间编写,因为必须显式添加安全证明注释”——这种特性难以推广(即便其价值显而易见);而“仅为解析文件就必须学习并集成另一门语言”同样难以被接受。
这并非否定其价值(未实际使用过我无法断言),只是其推广受阻实属必然。
若需解析不可信数据,某种程度的运行时检查实属必要。而Rust在类型系统中巧妙实现了“我已验证此项故无需重复检查”的编码机制。
网站上的图表对我而言无法显示,除非点击查看
我不理解这里展示的图表。首张图表显示“新增内存不安全代码”与“内存安全漏洞”数据缺乏稳定基准——两者数量在2019年前显然已呈下降趋势。不过无所谓,我们看到两者在2022年都出现了大幅下降。
而在下一张展示“Rust”和“C++”的图表中,我们发现2022年编写的C++代码量实际上有所增加,而Rust并未获得显著增长势头。
如何将这两组数据强行关联,得出Rust竟能修复“内存安全漏洞”的结论?C++代码量的增长竟导致“新增内存不安全代码”与“内存安全漏洞”双双减少?
此外“这种方法不仅能修复问题,还能帮助我们加快开发速度”的表述堪称AI红旗。
> 究竟如何将这两组数据强行关联,得出Rust竟能修复“内存安全漏洞”的结论?C++代码量增加反而导致“新增内存不安全代码”和“内存安全漏洞”数量双双下降,这岂非矛盾?
第一张图表对比的是
<内存不安全>与<内存安全>语言,而第二张图表对比的是C++与Rust。第一张图表中涉及的语言不止这两种。此外,第一张图表采用百分比形式,第二张图表则采用绝对值形式。
2022年似乎新增了大量非Rust的内存安全代码,如Java/Python等。
> 此外“这种方法不仅能修复问题,还能帮助我们加快进度”这句话是AI的典型特征。
这完全是人类的表达方式啊哈哈。
我有点困惑,为什么每次Rust编译成功就会有博客文章报道。我原以为Ada语言——尤其搭配证明工具使用时——历史更悠久且更稳健。实在难以判断Rust庞大的推广预算究竟是危险信号还是有趣的社会学案例,真希望知道真相。
Rust兼具两大特性:卓越的技术优势与强大的宣传机器。事实上它既非恶魔,也非划时代的完美产物。其足够的吸引力推动工具链与开发体验持续优化,适用于广泛场景。Rust在1.0版本前就因表现不俗而获得关注,这激励社区将其打磨至1.0版本的卓越境界,自此不断创造历史。我认为Rust能形成临界规模,源于其宏大的愿景和对开发者体验的专注——这种专注促成了对其性能与健壮性的持续改进。Ada语言同样出色,但它不那么华丽,也不标榜自己。从这个角度看,Rust是为炒作而生的,但这种关注也让语言本身获益匪浅。
> 这两组数据如何能佐证Rust能修复“内存安全漏洞”?
代码库中还包含Kotlin和Java
[已删除]
如今Rust的年龄已超过《K&R C语言编程》出版时Windows、Linux和NextStep系统诞生的年代。尽管C语言诞生更早,但直到该书问世后才广为人知…更不用说C++了
他们已经写过相关报道了?
https://security.googleblog.com/2023/09/scaling-rust-adoptio…
https://opensource.googleblog.com/2023/06/rust-fact-vs-ficti…
还有其他帖子。
别让伦杜克期刊看到这篇帖子,他可能会气得中风。