uutils/coreutils2026-08-01method 2.0
Rust adoption decisionRust 采用决策 · Rust · GNU coreutils reimplementation · 251,003 lines of Rust across 631 files · 108 utility binaries · shipped as the Ubuntu 25.10 defaultRust · GNU coreutils 重写 · 251,003 行 Rust,631 个文件 · 108 个工具二进制 · 已作为 Ubuntu 25.10 默认发布
PARTIAL
replace one utility at a time, not the userland at once一次替换一个工具,不要整套 userland 一起翻
Authorization授权 · APPROVE

The safety case holds and someone else already wrote the code: 251,003 lines of Rust across 109 utilities, with the whole residual unsafe surface enumerable as 275 `unsafe {` blocks and 4 `unsafe fn`. G1, G2 and G3 pass. G4 fails at the larger scope — the simultaneous whole-userland default flip, which nobody gated on per-utility behavioural acceptance. Both things that broke in the field are the kind a per-utility gate catches: cksum up to 17× slower than GNU on large files, and md5sum behaviour differences that broke Makeself self-extracting installers. The instrument was in the tree the whole time. It just was not blocking.安全性成立,而且代码别人已经写完了:251,003 行 Rust,覆盖 109 个工具,残留的 unsafe 面可以数清——275 个 `unsafe {` 块加 4 个 `unsafe fn`。G1、G2、G3 通过。G4 在更大的范围上失败:整套 userland 默认值同时翻转,翻转前没有把逐工具的行为验收设成阻塞条件。现场炸出来的两件事,都是逐工具验收门能拦下的:cksum 处理大文件时比 GNU 慢至多 17×,md5sum 的行为差异弄坏了 Makeself 自解压安装包。验收工具一直就在仓库里。只是没设成阻塞。

Conditional. The authorization holds only while every utility carries its own acceptance record. Re-open it if a utility reaches a default flip without a byte-level output diff and a throughput comparison against the incumbent. Also re-open it if the promotion order stops being derived from exposure, or if the GNU pass count stays outside the repository, where a distributor cannot read parity per release.有条件。只有每个工具都带着自己的验收记录,这份授权才成立。某个工具没有字节级输出 diff、也没有与现任实现的吞吐对比就翻默认值——重新评估。晋级顺序不再按暴露面推导,或者 GNU 通过数继续留在仓库之外、发行方按版本读不到 parity——同样重新评估。

Scope范围 one utility at a time, each behind its own acceptance gate一次换一个工具,每个都要过自己的验收门 Confidence置信度 MEDIUM Robustness稳健性 CONDITIONAL cli-quick · 108 independent single-purpose binaries behind a 40-year behavioural contractcli-quick · 108 个独立的单一用途二进制,背后是 40 年的行为契约
Rust already writtenRust 已经写好
251,003lines
631 .rs files at 0c8a3c7 · the adopter funds none of it0c8a3c7 上 631 个 .rs 文件 · 采用方一分钱不出
Independent acceptance gates独立验收门
109utilities个工具
directories under src/uu/ · one binary, one contract eachsrc/uu/ 下的目录 · 一个二进制,一份契约
Residual unsafe surface残留 unsafe 面
275blocks个块
`unsafe {` across *.rs · plus 4 `unsafe fn`*.rs 中的 `unsafe {` · 另有 4 个 `unsafe fn`
Acceptance material in tree仓库自带的验收材料
174,220lines
tests/ · 821 tracked paths · GNUmakefile + GnuTests.ymltests/ · 821 个受控路径 · GNUmakefile + GnuTests.yml
GNU parity at announcement公告时的 GNU parity
500 / 600tests项测试
case library figure, announcement-time, not current案例库数据,公告时点,不是当前值
Worst field regression现场最严重的回退
17× slower× 慢
cksum on large files vs GNU, later patched · phoronix.com大文件上的 cksum 对比 GNU,后来已修 · phoronix.com

01The options备选方案

Same objective for every option: remove the memory-unsafety class from the default command-line userland without changing the observable behaviour that scripts, Makefiles and installers depend on.每个方案的目标一致:把内存不安全这一类缺陷从默认命令行 userland 中移除,同时不改变脚本、Makefile 和安装程序依赖的可观测行为。

Option方案Scope范围Benefit收益Cost成本Time to value见效时间Compatibility / rollback兼容 / 回滚Evidence证据
Keep the GNU C userland and harden it保留 GNU C userland,继续加固retain · the correct default for every utility that has not passed its gate保留 · 对每个尚未过门的工具,这就是正确的默认值STAYno class removal; incidence work only不消除类;只做个案修补none new; ongoing C patch load没有新增;C 的补丁负担持续already in place已经就位native · nothing to roll back原生 · 没有东西需要回滚MODERATE
Promote utilities one at a time, gated逐个工具晋级,每个都设门recommended · the gate the field regressions would have failed推荐 · 现场那几个回退过不了的正是这道门PARTIALclass eliminated in each utility that passes its gate每过一道门,那个工具里的类就消失per-utility acceptance; dual packaging逐工具验收;双份打包one utility一个工具per-utility default · switch one back逐工具默认值 · 单个切回MODERATE
Flip the whole userland default at once一次性翻转整套 userland 默认值exclude · fails G4: acceptance was not gated per utility before the flip排除 · G4 不过:翻转前没有按工具设验收门MIGRATEthe same per-utility class removal, delivered as one correlated event逐工具消除的类一模一样,只是打包成一次相关联的事件交付cheap to ship; expensive to triage发布便宜;排查昂贵one release一个发行版本whole userland · rollback is a release整套 userland · 回滚等于再发一版STRONG
Adopt a short list of utilities only只采用一份短名单里的工具retain · the floor of the selected option, and its fallback保留 · 推荐方案的下限,也是它的退路EXTRACTclass removed in the few utilities chosen只在选中的少数工具里消除该类acceptance for a short list只为短名单做验收one release一个发行版本small surface · easy rollback面小 · 回滚容易MODERATE
Ship both; GNU stays the default两套都发;GNU 仍是默认retain · phase one of the selected option, and how the gate gets its evidence保留 · 推荐方案的第一阶段,也是这道门取证的方式ADOPTclass removed only for users who opt in只有主动切过去的用户才消除该类package both; no default change打两份包;默认值不动one release一个发行版本opt-in only · nothing to roll back仅选择性启用 · 没有东西需要回滚MODERATE
Defer everything until full GNU parity等 GNU 完全 parity 之后再说exclude · the flip's scope error, inverted排除 · 把翻转的范围错误反过来犯一次STAYno class removal until the last test passes最后一项测试通过之前,什么也不消除unbounded wait; class stays reachable无限期等待;该类始终可达unknown未知maximal margin · nothing ships余量最大 · 什么都发不出去WEAK

02Evidence ledger证据账本

States are option-scoped evidence, not additive points. Every repository claim was measured on the Rust candidate at the commit named in the methodology. The incumbent GNU C implementation was not cloned, and no claim is made about its internals.每个状态都是绑定到具体方案的证据,不是可以相加的分数。仓库层面的每一项数据,都测自方法一节列出的那个 commit 上的 Rust 候选实现。现任的 GNU C 实现没有克隆,本报告不对它的内部作任何断言。

D1 · Requirement & ownership需求与归属
SUPPORTS · rust optionsMODERATE
The requirement comes from the distributor: resilience and safety for the default userland ahead of the LTS. The code that has to change is the manually managed C implementation of utilities that every script, Makefile and installer invokes.需求由发行方提出:在 LTS 之前,让默认 userland 更有韧性、更安全。要改的代码,是那套人工维护的 C 实现——每个脚本、每个 Makefile、每个安装程序都在调用它。
why-not-rust case library · uutils coreutils entry · https://www.phoronix.com/news/Ubuntu-Rust-Coreutils-Perf
D2 · End-to-end reach端到端占比
N/ASTRONG
Nobody asserts a performance requirement here. The stated driver is resilience and safety, not latency. There is no end-to-end time share to compute, and deriving one from line counts would be a method error.这里没有人提出性能要求。发行方给出的驱动力是韧性与安全,不是延迟。没有端到端时间占比可算,从代码行数里推一个出来属于方法错误。
objective is safety and resilience, not latency
D3 · Tail & runtime尾部与运行时
N/ASTRONG
Neither implementation carries a managed runtime or a collector, so there is no runtime mechanism in any tail to remove.两边都不带托管运行时,也都不带 GC,尾部里没有可以摘掉的运行时机制。
ahead-of-time compiled native code on both sides
D4 · Fleet footprint机队占用
N/ASTRONG
These utilities are short-lived processes inside someone else's workload. The decision changes no fleet density, no instance count, no steady-state footprint.这些工具是别人负载里的短命进程。这个决策不改变机队密度,不改变实例数,也不改变稳态占用。
per-invocation process model
D5 · Startup shape启动形态
UNKNOWN · per-utility-rollout, userland-flipUNKNOWN
Coreutils is invoked once per command, so per-invocation cost is the shape that matters. Neither implementation was decomposed here into process start, initialization, I/O and useful work.coreutils 每条命令启动一次,因此单次调用的开销才是要看的形态。这里没有对任何一边做过进程启动、初始化、I/O 与有效工作的拆分测量。
no per-utility invocation profile measured
D6 · Safety & correctness安全与正确性
SUPPORTS · rust optionsSTRONG
You can enumerate the entire surface on which a memory-unsafety defect remains possible: 275 `unsafe {` blocks and 4 `unsafe fn` across 631 .rs files and 251,003 lines. 34,778 of those lines sit in the shared src/uucore/ layer, where the syscall work concentrates.还可能出现内存不安全缺陷的整个面,可以逐个列出来:631 个 .rs 文件、251,003 行里,275 个 `unsafe {` 块和 4 个 `unsafe fn`。其中 34,778 行在共享层 src/uucore/,系统调用的活儿集中在那里。
`unsafe {` 275 · `unsafe fn` 4 · *.rs at 0c8a3c7 · src/uucore/ 34,778 lines across 82 files
D7 · Concurrency & invariants并发与不变量
NEUTRAL · allMODERATE
These are single-purpose stream and filesystem transformations, invoked one command at a time. Neither language blocks data-parallel work. The invariants at stake here are behavioural, not concurrent.这些是单一用途的流与文件系统变换,一次一条命令。两种语言都没有挡住数据并行。这里真正要守的不变量是行为上的,不是并发上的。
per-invocation single-process utility model
D8 · Distribution分发
SUPPORTS · per-utility-rollout, gnu-plus-selected, dual-ship-optinMODERATE
The unit of distribution is one utility. There are 109 directories under src/uu/, of which 108 declare a [[bin]] target. So the packaging system can carry a mixed userland and roll one utility back without touching the other 107.分发的单位就是一个工具。src/uu/ 下有 109 个目录,其中 108 个声明了 [[bin]] 目标。打包系统因此可以承载一套混合 userland,回滚某一个工具而不动其余 107 个。
src/uu/ · 109 utility directories · 118,595 lines across 855 tracked files
D9 · Ecosystem & alternatives生态与替代品
SUPPORTS · per-utility-rollout, gnu-plus-selectedSTRONG
The Rust implementation already exists. Upstream maintains it. It covers 108 utilities, carries 174,220 lines of tests across 821 tracked paths, and has already shipped as a distribution default. No adopter has to write it or fund it.Rust 实现已经存在。上游在维护。它覆盖 108 个工具,带着 821 个受控路径、174,220 行测试,并且已经作为某个发行版的默认值发布过。采用方既不用写,也不用出钱。
251,003 lines of .rs · tests/ 174,220 lines across 821 tracked paths · Ubuntu 25.10 default
D10 · Boundary & compatibility边界与兼容
DISFAVORS · userland-flipSTRONG
The compatibility contract is 40 years of accreted observable behaviour, not an API: exit codes, byte-level output, and localized message text across 212 .ftl files. It broke in the field. cksum ran up to 17× slower than GNU on large files, later patched. base64 was slower before it was made faster. And md5sum behaviour differences broke Makeself self-extracting installers.兼容契约是 40 年累积下来的可观测行为,不是一套 API:退出码、字节级输出,以及 212 个 .ftl 文件里的本地化消息文本。它在现场断了。cksum 处理大文件时比 GNU 慢至多 17×,后来打了补丁。base64 也曾更慢,之后才被优化回来。md5sum 的行为差异弄坏了 Makeself 自解压安装包。
https://www.phoronix.com/news/Ubuntu-Rust-Coreutils-Perf · 212 .ftl localization files at 0c8a3c7
D11 · Delivery economics交付经济性
SUPPORTS · per-utility-rollout, gnu-plus-selectedMODERATE
The reimplementation cost is sunk upstream, and what remains for the adopter divides by utility. The GNU compatibility harness ships inside the repository: GNUmakefile, .github/workflows/GnuTests.yml and .github/workflows/GnuComment.yml. Making acceptance blocking is a CI configuration change. Nobody has to build an instrument.重写成本已经在上游沉没,采用方剩下的成本按工具切分。GNU 兼容测试套件就在仓库里:GNUmakefile、.github/workflows/GnuTests.yml、.github/workflows/GnuComment.yml。把验收设成阻塞,只是改一处 CI 配置。没人需要造新工具。
GNUmakefile · .github/workflows/GnuTests.yml · .github/workflows/GnuComment.yml
D12 · Counterfactual反事实
NEUTRAL · gnu-stay vs per-utility-rolloutMODERATE
The in-stack alternative and the Rust option are not competing programmes. The C implementation stays installed. It remains the default for every utility that has not passed its gate. Maintaining and hardening it is a precondition of the rollout, not an alternative to it.栈内的替代方案和 Rust 方案不是互相竞争的两个项目。C 实现继续装在系统里。对每一个尚未过门的工具,它继续是默认。继续维护和加固它,是这次推进的前置条件,不是它的替代品。
selected option keeps the incumbent as the per-utility default

N/A lenses: D2 end-to-end reach, D3 tail behaviour and D4 fleet footprint carry no part of this objective. The stated driver is resilience and safety, not latency or density. Neither implementation has a managed runtime. No collector either. Inventing a time share from line counts to fill D2 would be a method error.N/A 的维度:D2 端到端占比、D3 尾部行为、D4 机队占用,都不承担这个目标的任何部分。发行方给出的驱动力是韧性与安全,不是延迟或密度。两边都没有托管运行时。也都没有 GC。从代码行数里编一个时间占比来填 D2,属于方法错误。

03What decided it决定性证据

275 unsafe blocks is a number anyone can check275 个 unsafe 块,是谁都能自己数的数字

The residual unsafe surface of this userland is 275 `unsafe {` blocks and 4 `unsafe fn` across 631 .rs files and 251,003 lines. That is the safety payoff. You can re-run the count at a commit. A claim that a language is safe cannot be checked at all. Android's programme reports roughly 4% of its Rust lines as unsafe; that is a line share against a block count, so the metrics differ. Both still describe a small reviewable subset instead of a whole program.这套 userland 残留的 unsafe 面,是 631 个 .rs 文件、251,003 行里的 275 个 `unsafe {` 块和 4 个 `unsafe fn`。这就是安全收益。这个数在任意 commit 上都能重新跑一遍。「这门语言是安全的」这种说法根本没法核对。Android 的项目报告称其 Rust 代码约 4% 的行是 unsafe;那是行占比,这里是块计数,两个指标不一样。但两者描述的都是一小块可评审的子集,而不是整个程序。

`unsafe {` 275 · `unsafe fn` 4 · *.rs at 0c8a3c7 · blog.google Rust-in-Android

What failed in the field was behaviour, not memory safety现场出事的是行为,不是内存安全

Three documented regressions shipped with the default. cksum ran up to 17× slower than GNU on large files, later patched. base64 was slower before it was made faster. md5sum behaviour differences broke Makeself self-extracting installers. None of the three is a memory-safety defect. Forty years of GNU tuning is one baseline and forty years of accreted observable behaviour is another; a rewrite inherits neither.跟着默认值一起发出去的,是三个有据可查的回退。cksum 处理大文件时比 GNU 慢至多 17×,后来打了补丁。base64 曾经更慢,之后才优化回来。md5sum 的行为差异弄坏了 Makeself 自解压安装包。三个里没有一个是内存安全缺陷。GNU 四十年的调优是一条基线,四十年累积的可观测行为是另一条;重写这两条都继承不到。

https://www.phoronix.com/news/Ubuntu-Rust-Coreutils-Perf

108 utilities are 108 independent acceptance gates108 个工具,就是 108 道彼此独立的验收门

The module boundary here is a process boundary. 109 directories sit under src/uu/, and 108 of them declare a [[bin]] target. Each shipping utility therefore has its own binary and its own behavioural contract, so promotion, acceptance and rollback all happen one utility at a time. PARTIAL is the scope word that structure produces. The per-utility rollout can ship today. The simultaneous flip cannot.这里的模块边界就是进程边界。src/uu/ 下有 109 个目录,其中 108 个声明了 [[bin]] 目标。因此每个出货的工具都有自己的二进制、自己的行为契约,晋级、验收、回滚都是一次一个。PARTIAL 这个范围词,是这套结构自己给出来的。逐工具推进今天就能发。整套同时翻不能。

src/uu/ · 109 utility directories · 118,595 lines across 855 tracked files

The acceptance instrument was already in the tree验收工具本来就在仓库里

The gate was never missing. GNUmakefile, .github/workflows/GnuTests.yml and .github/workflows/GnuComment.yml run the GNU compatibility suite in CI, backed by 174,220 lines of tests across 821 tracked paths. Nobody made it blocking per utility before the default changed. That one omission is the whole distance between this report's PASS and the flip's FAIL at G4.这道门从来不缺。GNUmakefile、.github/workflows/GnuTests.yml 和 .github/workflows/GnuComment.yml 在 CI 里跑 GNU 兼容套件,背后是 821 个受控路径、174,220 行测试。只是在默认值改变之前,没有人把它按工具设成阻塞。这一处遗漏,就是本报告的 PASS 与那次翻转在 G4 上 FAIL 之间的全部距离。

GNUmakefile · .github/workflows/GnuTests.yml · .github/workflows/GnuComment.yml · tests/ 821 tracked paths

The repository does not state a GNU pass count仓库里没有 GNU 通过数

README.md:355 is the section heading 'GNU test suite compatibility'. It links out to uutils.org coverage documentation and an evolution chart. The pass count is tracked externally, not in the tree. The roughly 500 of 600 figure this report carries is an announcement-time number, and the repository holds no current number to replace it. A distributor has to go and fetch parity. It cannot be read here.README.md:355 是标题「GNU test suite compatibility」。它把链接指向 uutils.org 的覆盖率文档和一张演进图。通过数记在仓库外面,不在树里。本报告带的大约 500 / 600 是公告时点的数字,仓库里没有当前数字可以替换它。发行方得自己去外部取 parity。在这里读不到。

README.md:355 · announcement-time figure from the why-not-rust case library

04What Rust buys here, and what it doesn'tRust 在这里买得到什么,买不到什么

Buys买得到

  • Class elimination per promoted utility每晋级一个工具,就消掉一类缺陷once a utility passes its gate, memory-unsafety stops being reachable in the code path that every script invoking it uses.工具一旦过门,调用它的每个脚本所走的代码路径上,内存不安全就不再可达。
  • An unsafe surface you can audit一个审计得完的 unsafe 面275 `unsafe {` blocks and 4 `unsafe fn` across 251,003 lines is a review list, not a codebase-wide condition.251,003 行里 275 个 `unsafe {` 块和 4 个 `unsafe fn`,那是一份评审清单,不是整个代码库的状态。
  • Per-utility blast radius按工具收敛的爆炸半径108 separate binaries mean one regression is one default switch, not a release-wide rollback.108 个各自独立的二进制,意味着一个回退只需切一个默认值,而不是整版回滚。

Doesn't buy买不到

  • Speed速度the objective is resilience, not latency, and the field evidence runs the other way — cksum was up to 17× slower than GNU on large files before it was patched.目标是韧性,不是延迟,而且现场证据方向相反——打补丁之前,cksum 处理大文件比 GNU 慢至多 17×。
  • Inherited behaviour继承来的行为exit codes, byte-level output and localized messages across 212 .ftl files are a 40-year accretion; a rewrite re-derives all of it rather than inheriting it, and md5sum differences broke Makeself installers.退出码、字节级输出,以及 212 个 .ftl 文件里的本地化消息,是 40 年攒下来的;重写只能把它们重新推导一遍,继承不了,md5sum 的差异就弄坏了 Makeself 安装包。
  • Freedom from the acceptance work省掉验收这件事the announcement-time figure was roughly 500 of 600 GNU tests passing and the repository states no current count; the gate is the work, not a formality.公告时点的数字是 GNU 测试通过约 500 / 600,仓库里没有当前数;这道门本身就是工作量,不是走个形式。

05Who has done this before同型先例

Trifecta · sudo-rs

MIGRATED

A scoped, privileged C tool was replaced in Rust and shipped as an Ubuntu 25.10 default. The stated rationale was attack-surface reduction. No first-party material frames it as a performance win.一个范围明确的特权 C 工具被 Rust 重写,作为 Ubuntu 25.10 的默认发布。给出的理由是缩小攻击面。第一方材料里没有任何地方把它包装成性能收益。

matches匹配: same distribution, same release, same C-to-Rust safety objective, same refusal to argue speed同一个发行版,同一个版本,同样的 C 转 Rust 安全目标,同样不拿速度说事 · differs不匹配: one binary with one behavioural contract rather than 109, and a privilege boundary rather than an output contract scripts parse只有一个二进制、一份行为契约,不是 109 份;守的是权限边界,不是脚本要解析的输出契约
first-party · project blog

Google · Android memory-safety programme

INCREMENTAL

Memory safety's share of Android vulnerabilities fell from 76% to below 20%. The old C/C++ was not mass-rewritten. New code went into safe languages instead. The programme also reports roughly 4% of its Rust lines as unsafe, and a density of about 0.2 vulnerabilities per million Rust lines against about 1,000 for C/C++.内存安全类缺陷在 Android 漏洞中的占比,从 76% 降到 20% 以下。旧的 C/C++ 没有被大规模重写。新代码改用安全语言来写。该项目还报告,其 Rust 代码约 4% 的行是 unsafe,缺陷密度约为每百万行 Rust 0.2 个,而 C/C++ 约为 1,000 个。

matches匹配: same requirement class, and the closest published comparator for this repository's measured unsafe surface同一类需求,也是本仓库实测 unsafe 面最接近的公开参照 · differs不匹配: an operating system with far more entry points; the density ratio is C/C++-relative and does not transfer to memory-safe code, and a line share is not a block count那是一个入口点多得多的操作系统;密度比是相对 C/C++ 的,无法迁移到内存安全代码上,而且行占比不等于块计数
first-party · vendor security blog

fish shell 4.0 · C++ → Rust

MIGRATED

A whole widely used command-line program was ported to Rust. The maintainers graded it themselves. Performance is 'usually slightly better in terms of time taken', and memory use has a slightly higher floor and a lower ceiling. The 'half a year' estimate became about two years.一个使用广泛的命令行程序被整体移植到 Rust。维护者自己给出了结论。性能「usually slightly better in terms of time taken」,内存占用下限略高、上限更低。当初「half a year」的估计,最后变成了大约两年。

matches匹配: complete rewrite of a command-line program in the same direction, with a near-neutral performance lens stated first-party and a schedule that slipped fourfold同一方向上对命令行程序的完整重写,第一方自述的性能结论接近中性,工期滑了四倍 · differs不匹配: one shell with one contract and an opt-in user base, not a distribution's default userland with 109 contracts一个 shell、一份契约、用户自愿安装,不是带 109 份契约的发行版默认 userland
first-party · project blog

Bun · Zig → Rust port

PORTED · CONTESTED

About 535,000 lines were mechanically ported from Zig to Rust in roughly eleven days. The motive was a stream of use-after-free, double-free and leak defects. Selected first-party benchmarks moved 2.2–4.8% and binaries shrank about 20%. 19 regressions surfaced afterwards and were fixed.大约 535,000 行代码在约十一天内从 Zig 机械移植到 Rust。动机是一连串 use-after-free、double-free 和内存泄漏缺陷。第一方选取的基准测试变动 2.2–4.8%,二进制体积缩小约 20%。事后浮出 19 个回退,都已修复。

matches匹配: native-to-native port with no architecture change, safety-motivated, and regressions that only surfaced once real users ran it原生到原生的移植,架构不变,动机是安全,回退都是真实用户跑起来之后才出现的 · differs不匹配: Zig rather than C, no 40-year behavioural contract, not a distribution default, and production readiness was publicly contested at publication (https://www.theregister.com/devops/2026/07/14/zig-creator-calls-buns-claude-rust-rewrite-unreviewed-slop/5270743)源语言是 Zig 不是 C,没有 40 年的行为契约,也不是发行版默认;发布时其生产可用性遭到公开质疑(https://www.theregister.com/devops/2026/07/14/zig-creator-calls-buns-claude-rust-rewrite-unreviewed-slop/5270743)
first-party · vendor blog (contested)

06What to do next可逆路径

Make the in-tree GNU harness blocking per utility把仓库自带的 GNU 套件按工具设成阻塞

The distribution's coreutils package maintainers do this with uutils upstream. What comes out is a per-utility promotion record from GNUmakefile and .github/workflows/GnuTests.yml. Add a byte-level output diff for the documented flag set and a throughput comparison against the incumbent on large inputs. Three things must pass before a utility is eligible: its GNU test set, byte-identical output on the documented flags, and throughput inside a stated tolerance on large inputs. Miss the throughput comparison and the utility does not move. cksum shipped without it. Rollback is cheap here — CI configuration and documentation, no packaging change.由发行版的 coreutils 打包维护者与 uutils 上游一起做。产出是每个工具一份晋级记录,由 GNUmakefile 和 .github/workflows/GnuTests.yml 生成。再加上文档化选项集的字节级输出 diff,以及大输入下与现任实现的吞吐对比。一个工具要够格,三项都得过:GNU 测试集通过,文档化选项下输出逐字节一致,大输入下吞吐落在明示的容差内。缺了吞吐对比,这个工具就不动。cksum 当初发出去时,缺的就是它。这一步回滚很便宜——只涉及 CI 配置和文档,不动打包。

3–6 weeks3–6 周

Ship both implementations with the incumbent as default两套实现都发,现任实现仍为默认

Package maintainers make both userlands installable, allow selection per utility, and leave the incumbent as the default. They also open a named channel for behavioural reports, so the evidence has somewhere to land. A candidate utility passes once it has run a full release cycle of opt-in use with no new behavioural report against it. Drop any utility that accumulates reports faster than they get closed. No default changes at this step. There is nothing to roll back.打包维护者让两套 userland 都能装上,允许按工具选择,默认仍然是现任实现。同时开一个专门的渠道收行为问题报告,让证据有地方落。一个候选工具要过关,需要在选择性启用状态下跑满一个完整发布周期,期间没有新的行为问题报告。哪个工具的报告攒得比关得快,就把它撤下来。这一步不改任何默认值。没有东西需要回滚。

1 release cycle1 个发布周期

Promote utilities one at a time, ordered by exposure按暴露面排序,一次晋级一个工具

Package maintainers own the order, and each promoted utility gets a named owner of its own. The order comes from what breaks worst when behaviour changes, which puts checksum tools and anything installers invoke late rather than early. One change per utility, and no promotion bundles two. Each one carries its own gate record from step 1. If two consecutive utilities produce field regressions after passing their gate, freeze promotions — the gate is wrong, not the utility. Rolling back is one default switch. The other 107 are untouched.顺序由打包维护者定,每个晋级的工具另有一位具名负责人。排序依据是行为一变会坏得最狠的地方,因此校验和类工具、以及安装程序会调用的东西都排在后面,不排在前面。一个工具一次变更,不允许一次晋级捆两个。每次晋级都带着第 1 步产出的门记录。如果连着两个工具过了门之后仍在现场出回退,就冻结晋级——错的是门,不是工具。回滚就是切一个默认值。其余 107 个不受影响。

per utility, scoped按工具计,范围明确

Publish the pass count where the decision can read it把通过数发布在做决策的人读得到的地方

uutils upstream and the distributor record the GNU test pass count, and the names of the failing tests, per release in the repository. README.md:355 currently links that data out to external coverage documentation, so a distributor reading the tree alone sees nothing. Acceptance is simple: the count and the failing set appear in the release artifact the distributor already consumes. If the count cannot be produced per release, parity is UNKNOWN and nothing else gets promoted. This step touches documentation and release tooling. Nothing else.由 uutils 上游与发行方一起,把 GNU 测试的通过数和失败用例名,按版本记进仓库。README.md:355 现在把这份数据链到外部覆盖率文档,只读树的发行方什么也看不到。验收标准很简单:这个数和失败集合出现在发行方本来就在消费的发布产物里。如果做不到按版本给出这个数,parity 就是 UNKNOWN,后面不再晋级任何工具。这一步只动文档和发布工具链。别的都不动。

1–2 weeks1–2 周

Re-run these gates before the LTS在 LTS 之前把这几道门再跑一遍

The distributor's release team owns this one. The output is a short re-assessment listing which utilities were promoted, which gate records exist, and which field regressions turned up after promotion instead of before it. It passes when it names an owner for every promoted utility and the residual failing tests that utility carries into the LTS. Any utility without a gate record goes back to the incumbent default before the LTS freeze. The rollback mechanism is unchanged from step 3: one default switch per utility.这一步归发行方的发布团队。产出是一份简短的复评,列出哪些工具已晋级、哪些门记录存在、哪些现场回退是在晋级之后而不是之前才发现的。复评过关的条件是:为每个已晋级的工具点名负责人,并写明它带进 LTS 的残余失败用例。任何没有门记录的工具,在 LTS 冻结前退回现任默认实现。回滚机制与第 3 步相同:一个工具切一个默认值。

1–2 days per review每次复核 1–2 天

07Arguing both sides对称反证

Against migrating反驳「迁移」

PASSEnd-to-end reach端到端占比No Rust option here claims a performance benefit. D2 is recorded N/A instead of being filled with a time share invented from line counts. The field evidence runs the other way.这里没有任何 Rust 方案声称性能收益。D2 记为 N/A,而不是拿代码行数编一个时间占比填进去。现场证据的方向恰好相反。
PASSAttribution归因The safety delta is attributed to the language property, and measured on the candidate itself. No redesign, algorithm change or packaging change is credited to Rust.安全收益归因于语言属性,并且是在候选实现自身上测出来的。没有把任何重新设计、算法改动或打包改动记到 Rust 头上。
HITBaseline and regime基线与口径The incumbent was never measured. GNU coreutils was not cloned, so the size of the class being removed rests on one structural fact: the incumbent is manually managed C. The parity figure carried here is an announcement-time number.现任实现从未被测量。GNU coreutils 没有克隆,因此被移除那一类缺陷有多大,只靠一个结构性事实支撑:现任实现是人工维护的 C。本报告带的 parity 数字来自公告时点。
HITBoundary and omitted cost边界与被漏掉的成本The original decision omitted two costs: the compatibility surface, and a second upstream's fix latency. md5sum behaviour differences broke Makeself installers. The cksum and base64 fixes arrived after the flip, not before it.原来那个决策漏掉了两项成本:兼容面,以及第二个上游的修复时延。md5sum 的行为差异弄坏了 Makeself 安装包。cksum 和 base64 的修复是在翻转之后到的,不是之前。
PASSDelivery ownership交付归属Every promotion carries a named owner, a gate record and a per-utility rollback. The larger option is excluded on this check, not on its technology.每次晋级都带着具名负责人、门记录和逐工具回滚。更大的那个方案是在这一项上被排除的,不是因为它用什么技术。

Against staying反驳「不动」

PASSFunded counterfactual有资源的反事实Continued maintenance of the C implementation is retained as a live option, not dismissed. It stays the default for every utility that has not passed its gate.继续维护 C 实现,是作为一个仍然有效的选项保留下来的,没有被打发掉。对每个尚未过门的工具,它继续是默认值。
HITCost of inaction不作为的代价Keeping a manually managed C userland keeps the memory-unsafety class reachable in code that every script, Makefile and installer on the system invokes.继续用人工维护的 C userland,就是让内存不安全这一类缺陷,在系统上每个脚本、每个 Makefile、每个安装程序都会调用的代码里保持可达。
HITMaturity mistaken for safety把成熟度当成安全性The staying case leans on forty years of GNU history. That history is evidence about tuning and observable behaviour, not about the memory-unsafety class. Age does not make C memory-safe.不动的理由靠的是 GNU 四十年的历史。这段历史能证明的是调优和可观测行为,证明不了内存不安全这一类缺陷。年头长不会让 C 变得内存安全。
PASSNative-advantage denial否认原生方案的优势The candidate's advantages are recorded, not waved away. It exists, covers 109 utilities, carries 174,220 lines of tests, has already shipped as a default, and its unsafe surface is enumerable.候选实现的优势被记下来了,没有一笔带过。它已经存在,覆盖 109 个工具,带着 174,220 行测试,已经作为默认发布过,而且它的 unsafe 面可以逐个列清。
PASSStop condition停止条件Staying is per utility and bounded by a gate. It is not open-ended. Each utility leaves the staying option the moment its record clears. 'Harden C forever' is never the default answer.「不动」是按工具算的,而且有门卡着。它不是无限期的。任何一个工具,只要记录齐了就离开「不动」这一栏。「永远加固 C」从来不是默认答案。

08How this was produced方法与记录

uutils/coreutils at 0c8a3c7 · static read-only analysis · why-not-rust method 2.0uutils/coreutils 于 0c8a3c7 · 只读静态分析 · why-not-rust 方法 2.0

Repository: github.com/uutils/coreutils at commit 0c8a3c7, shallow clone, 1,967 tracked files. Scope: the whole Rust candidate repository, with one utility under src/uu/ as the unit of decision. The measurement target is the Rust candidate, not the incumbent. GNU coreutils was not cloned, so no claim is made about its internals, and that limit is why confidence is MEDIUM rather than HIGH. Sampling: 251,003 lines across 631 .rs files; src/uu/ 118,595 lines across 855 tracked files in 109 utility directories; src/uucore/ 34,778 lines across 82 files; tests/ 174,220 lines across 821 tracked paths; 212 .ftl localization files; 275 `unsafe {` blocks and 4 `unsafe fn` across *.rs; the GNU compatibility harness present as GNUmakefile with .github/workflows/GnuTests.yml and .github/workflows/GnuComment.yml. README.md:355 is the 'GNU test suite compatibility' heading and links the pass count out to external coverage documentation, so no in-repository pass count is quoted here. The roughly 500 of 600 figure is an announcement-time number from the skill's case library, not a current measurement. No build, test, benchmark or network call was run against the project. Objective: remove the memory-unsafety class from the default command-line userland without changing the observable behaviour scripts, Makefiles and installers depend on. User-supplied facts: none. No Amdahl calculation appears and none should. The objective is resilience and safety, not latency, so D2 is recorded N/A; converting a line share into a time share to fill it would be a method error, and the one measured performance fact runs against the migration rather than for it. Why the selected option is the smallest sufficient step: the module boundary here is a process boundary. 109 directories sit under src/uu/, of which 108 declare a [[bin]] target, so 108 shipping utilities are 108 separate binaries with 108 separate behavioural contracts. Per-utility promotion delivers the same class elimination as the simultaneous default flip, and it keeps rollback at one utility. External figures keep their URL and regime: the cksum, base64 and md5sum regressions are press coverage of the Ubuntu 25.10 release (https://www.phoronix.com/news/Ubuntu-Rust-Coreutils-Perf); Android's unsafe-line share is a vendor security-blog figure, and a line share rather than a block count. This framework compares explicit options through four non-compensatory gates. It is a structured decision protocol, not a statistical predictor.仓库:github.com/uutils/coreutils,commit 0c8a3c7,浅克隆,1,967 个受版本控制的文件。范围:整个 Rust 候选仓库,决策单位是 src/uu/ 下的一个工具。测量对象是 Rust 候选实现,不是现任实现。GNU coreutils 没有克隆,因此不对它的内部作任何断言;正是这个限制让置信度停在 MEDIUM 而不是 HIGH。采样:631 个 .rs 文件共 251,003 行;src/uu/ 下 109 个工具目录、855 个受控文件、118,595 行;src/uucore/ 82 个文件、34,778 行;tests/ 821 个受控路径、174,220 行;212 个 .ftl 本地化文件;*.rs 中 275 个 `unsafe {` 块和 4 个 `unsafe fn`;GNU 兼容测试套件以 GNUmakefile 加 .github/workflows/GnuTests.yml 和 .github/workflows/GnuComment.yml 的形式存在。README.md:355 是「GNU test suite compatibility」标题,把通过数链到外部覆盖率文档,因此这里不引用任何仓库内的通过数。大约 500 / 600 这个数字来自技能自带的案例库,是公告时点的数,不是当前测量值。没有对项目执行任何构建、测试、基准或网络调用。目标:把内存不安全这一类缺陷从默认命令行 userland 中移除,同时不改变脚本、Makefile 和安装程序依赖的可观测行为。用户提供的事实:无。报告里没有 Amdahl 计算,也不该有。目标是韧性与安全,不是延迟,所以 D2 记为 N/A;把行占比换算成时间占比来填这一格属于方法错误,而唯一一项实测的性能事实,方向是反对迁移而不是支持迁移。为什么推荐方案就是最小充分步骤:这里的模块边界就是进程边界。src/uu/ 下有 109 个目录,其中 108 个声明了 [[bin]] 目标,于是 108 个出货工具就是 108 个各自独立的二进制、108 份各自独立的行为契约。逐工具晋级带来的类消除,与整套默认值同时翻转完全一样,同时把回滚控制在一个工具的粒度上。外部数据保留其 URL 与口径:cksum、base64 和 md5sum 的回退来自对 Ubuntu 25.10 发布的媒体报道(https://www.phoronix.com/news/Ubuntu-Rust-Coreutils-Perf);Android 的 unsafe 行占比来自厂商安全博客,是行占比,不是块计数。这个框架通过四道非补偿性证据门比较明确列出的方案。它是一套结构化决策协议,不是统计预测器。

What we don't know证据缺口What it would change会改变什么
Per-utility acceptance records for the utilities already flipped已经翻过默认值的那些工具,缺逐工具验收记录Without them the residual risk of the shipped default is unknown per utility, and the gate cannot be applied retroactively without the byte-level diffs and throughput comparisons it needs.没有它们,已发布默认值的残余风险就无法按工具说清;而缺了所需的字节级 diff 和吞吐对比,这道门也没法事后补做。
A GNU test pass count published per release按版本公布的 GNU 测试通过数README.md:355 links the coverage data out to external documentation, so a distributor reading the repository alone cannot see parity. Parity stays an announcement-time figure, and the parity-first option cannot even be evaluated.README.md:355 把覆盖率数据链到外部文档,只读仓库的发行方看不到 parity。parity 因此停留在公告时点的数字上,而「先等 parity」那个方案连评估都无从谈起。
Per-invocation cost decomposition for either implementation两边实现都缺单次调用的开销拆分D5 stays UNKNOWN. A per-utility startup and throughput comparison is what turns a cksum-class regression from a field discovery into a pre-promotion gate.D5 保持 UNKNOWN。把 cksum 那类回退从「现场才发现」变成「晋级前就拦住」,靠的就是逐工具的启动与吞吐对比。
A measured comparison against the incumbent与现任实现的实测对比GNU coreutils was not cloned or measured here, so the eliminated-class size rests on structural argument and the residual-risk side of the counterfactual is unquantified.这里没有克隆也没有测量 GNU coreutils,因此被消除那一类缺陷有多大只能靠结构性论证,反事实中残余风险那一侧也没有量化。