rust-port
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseRust port
Rust 移植
The goal is the Rust a Rust developer would have written to solve the same problem, with
the same observable behavior. A line-by-line transliteration (C written in Rust syntax,
or Python with ) compiles but keeps every problem the original had and adds
the borrow checker on top. Port the behavior, not the structure.
Box<dyn Any>目标是产出 Rust 开发者为解决同一问题会编写的 Rust,并具有相同的可观察行为。逐行直译(用 Rust 语法书写 C,或用 编写 Python)虽然能够编译,却会保留原实现的所有问题,并额外背上借用检查器。应移植行为,而不是结构。
Box<dyn Any>1. Understand the original before writing anything
1. 编写代码前先理解原实现
- Read the code and its tests, and anything that calls it, if you have it.
- List the observable behavior: every public function, its inputs, its outputs, and every way it can fail. Include the quiet parts, which are where ports go wrong: ordering of results, how ties break, what happens on empty input, whitespace and case handling, integer limits, and what counts as an error versus a valid edge case.
- Note what is incidental, meaning things the original only does because of its
language: manual memory management, out-parameters, returns, global state, fixed-size buffers, a class that exists only to group functions, or
NULLchecks. These don't carry over.isinstance - If the original has a bug or an ambiguity, keep its behavior, flag it in the porting notes, and let the user decide. A port is not the place to silently fix things.
- 阅读代码及其测试;如果可以,也阅读所有调用它的代码。
- 列出可观察行为:每个公共函数、它的输入、输出以及所有可能的失败方式。还要包含那些不显眼的行为,因为移植时最容易在这些问题上出错:结果顺序、平局如何处理、空输入时会发生什么、空白字符和大小写如何处理、整数边界,以及哪些情况算错误、哪些属于有效的边缘情况。
- 记录语言层面的附带行为,也就是原实现仅因其语言特性而存在的内容:手动内存管理、输出参数、返回值、全局状态、固定大小缓冲区、仅用于组织函数的类,或
NULL检查。这些内容不应被移植。isinstance - 如果原实现存在缺陷或歧义,应保留其行为,在移植说明中指出,并交由用户决定。移植不应成为悄悄修复问题的地方。
2. Port the tests first
2. 先移植测试
Translate the original tests into Rust tests before porting the implementation. Keep one
Rust test per original test, with a name that maps back to it, so a reader can check
coverage. The tests are the definition of "same behavior". If the original has no tests,
write characterization tests from the behavior list in step 1.
在移植实现之前,先将原有测试翻译为 Rust 测试。每个原有测试都保留为一个对应的 Rust 测试,并使用能够映射回原测试的名称,方便读者检查覆盖率。测试定义了“相同行为”的含义。如果原实现没有测试,则根据第 1 步中的行为列表编写特征化测试。
3. Design the Rust API, then implement it
3. 先设计 Rust API,再实现
Decide the public types and signatures from the behavior list, not from the original's
shape. Then implement against the ported tests.
根据行为列表,而不是原实现的结构,确定公共类型和函数签名。然后按照移植后的测试进行实现。
From C and C++
从 C 和 C++ 移植
| In the original | In Rust |
|---|---|
| Owned types ( |
| |
Error codes ( | |
Out-parameters ( | Return values: put the data in the |
Flag bitmasks ( | An options struct with named fields and |
| |
| Pointer plus count, iterator by index | Slices, iterators |
| Fixed-size buffers and their truncation | Growable types; drop the limit unless the limit is behavior |
| |
| Global or static mutable state | A struct the caller owns |
| |
Don't write or use raw pointers unless the port must talk to C through FFI, and
then confine it as the skill describes.
unsaferust-best-practices| 在原实现中 | 在 Rust 中 |
|---|---|
| 所有权类型( |
表示“不存在”的 | |
错误码( | |
输出参数( | 返回值:将数据放入 |
标志位掩码( | 使用带命名字段并实现 |
| |
| 指针加数量、按索引迭代 | 切片、迭代器 |
| 固定大小缓冲区及其截断行为 | 可增长类型;除非大小限制本身属于行为,否则移除该限制 |
| |
| 全局或静态可变状态 | 由调用方拥有的 struct |
| 使用 |
除非移植必须通过 FFI 与 C 通信,否则不要编写 或使用原始指针;必须使用时,应按照 skill 的说明将其限制在最小范围内。
unsaferust-best-practicesFrom Python
从 Python 移植
| In the original | In Rust |
|---|---|
| An exception hierarchy | One error enum (or one per area) with a variant per exception class; data attributes become variant fields |
| |
Strings used as a closed set ( | An enum, with |
| Dicts or classes used as records | Structs with named fields |
| |
Duck typing, | Traits or enums |
Unbounded | A sized integer chosen on purpose, with overflow handled ( |
| |
| |
| |
| 在原实现中 | 在 Rust 中 |
|---|---|
| 异常继承体系 | 一个错误 enum(也可按领域拆分为多个),每个异常类对应一个变体;数据属性转换为变体字段 |
作为值使用的 | |
用作封闭集合的字符串( | enum,并为文本形式实现 |
| 用作记录结构的字典或类 | 带命名字段的 struct |
| 字典迭代顺序(保证为插入顺序) | 当顺序可观察时使用 |
Duck typing、 | trait 或 enum |
无界 | 有意识选择定长整数,并在输入可能很大时通过 |
| |
| |
在解析周围使用 | 使用带错误映射的 |
From Go, JavaScript, and TypeScript
从 Go、JavaScript 和 TypeScript 移植
- Go returns become
(value, error);Resultbecomesnil; interfaces become traits; goroutines and channels become threads withOptionor async tasks, depending on what the original needs.std::sync::mpsc - JavaScript and
nullbecomeundefined; thrown errors becomeOption; plain objects become structs; numbers become a sized integer orResult, chosen on purpose.f64
- Go 的 返回值转换为
(value, error);Result转换为nil;接口转换为 trait;goroutine 和 channel 根据原实现的需求转换为使用Option的线程或异步任务。std::sync::mpsc - JavaScript 的 和
null转换为undefined;抛出的错误转换为Option;普通对象转换为 struct;数字有意识转换为定长整数或Result。f64
4. Keep the port proportionate
4. 保持移植规模合理
A port that matches the original on every possible input is usually the wrong goal.
Match it on the inputs it actually receives, and use Rust's standard library for the rest.
- Don't reimplement the source language's runtime. Use ,
str::lines,str::trim, andstr::parsemethods, not hand-written copies of Python'schar,splitlines,strip, orint(), or of C's locale-dependentrepr()andisspace. Where Rust's behavior differs only on exotic input, such as Unicode digits, non-ASCII whitespace, or unbounded integers, list the difference in the porting notes instead of coding around it. Write a compatibility shim only when the user says those inputs occur.strtol - Don't grow the API. Port the public surface the original has, plus the standard
trait impls Rust callers expect (,
Debug,Clone,Display,Error,FromStr, andDefaultfor collections). Skip builders,IntoIteratorimpls,Index,FromIterator, and custom iterator types unless the original's callers need them.Extend - Keep the algorithm unless it's wrong. Swap in a different algorithm only to fix a real problem, such as quadratic behavior on realistic input, and say so in the notes.
- Check the size. Count code lines, excluding tests, comments, and doc comments. A port from C or C++ usually lands near the original's size. A port from Python or JavaScript usually lands at two to three times the original, because types, error enums, and their trait impls are explicit in Rust. Much more than that usually means one of the three habits above.
Differential testing against the original is worth doing, but a few dozen realistic and
edge-case inputs answer the question. A fuzzing harness over millions of inputs usually
just finds exotic divergences that belong in the notes.
要求移植在所有可能输入上都与原实现一致,通常并不是正确目标。应匹配实际接收到的输入,其余部分使用 Rust 标准库实现。
- 不要重新实现源语言的运行时。 使用 、
str::lines、str::trim和str::parse方法,不要手写 Python 的char、splitlines、strip或int(),也不要重写 C 中依赖 locale 的repr()和isspace。如果 Rust 的行为仅在特殊输入上存在差异,例如 Unicode 数字、非 ASCII 空白字符或无界整数,应在移植说明中列出差异,而不是编写代码规避。只有当用户明确表示会出现这些输入时,才编写兼容性 shim。strtol - 不要扩大 API。 移植原实现已有的公共接口,并补充 Rust 调用方期望的标准 trait 实现(、
Debug、Clone、Display、Error、FromStr,以及集合的Default)。除非原实现的调用方确实需要,否则不要添加 builder、IntoIterator实现、Index、FromIterator或自定义迭代器类型。Extend - 除非算法本身有问题,否则保留原算法。 只有为了解决真实问题时才替换算法,例如真实输入下存在二次方复杂度,并应在说明中指出。
- 检查代码规模。 统计代码行数时,不包括测试、注释和文档注释。从 C 或 C++ 移植时,代码规模通常接近原实现。从 Python 或 JavaScript 移植时,通常会达到原实现的两到三倍,因为 Rust 需要显式编写类型、错误 enum 及其 trait 实现。规模远超这一范围,通常意味着出现了上述三种不良习惯之一。
可以进一步进行差分测试,但几十个真实输入和边缘输入通常就足以回答是否保持一致的问题。对数百万个输入运行 fuzzing 框架,通常只会发现应在说明中记录的特殊差异。
5. Verify
5. 验证
Run these if a Rust toolchain is available, and fix everything before reporting:
bash
cargo build
cargo test
cargo clippy --all-targets -- -D warningsEvery ported test must pass. If the original's tests can be run too, run them and compare
results on the same inputs.
如果可以使用 Rust 工具链,请运行以下命令,并在报告结果前修复所有问题:
bash
cargo build
cargo test
cargo clippy --all-targets -- -D warnings每个移植后的测试都必须通过。如果原有测试也可以运行,则一并运行,并在相同输入下比较结果。
6. Write the porting notes
6. 编写移植说明
Alongside the code, write a short that a reviewer can check in a few
minutes:
PORTING.md- API mapping: a table from each original function or type to its Rust equivalent.
- Test mapping: each original test and the Rust test that covers it.
- Deliberate differences: anything that now behaves differently and why, such as an out-of-memory error that no longer exists, a fixed buffer limit that's gone, or exotic input where Rust's standard library behaves differently from the original's.
- Preserved quirks: odd behavior kept on purpose, so nobody "fixes" it by accident.
- Verification: which commands you ran and their result, or a plain statement that you couldn't run them.
For the style of the Rust itself, follow the skill if it is
installed.
rust-best-practices在代码旁编写一份简短的 ,以便审阅者能在几分钟内完成检查:
PORTING.md- API 映射: 使用表格将每个原有函数或类型映射到对应的 Rust 实现。
- 测试映射: 列出每个原有测试及其对应的 Rust 测试。
- 有意保留的差异: 记录所有行为发生变化的细节及其原因,例如不再存在的内存不足错误、被移除的固定缓冲区限制,以及 Rust 标准库对特殊输入的处理与原实现不同的情况。
- 保留的特殊行为: 记录有意保留的异常行为,避免其他人无意中将其“修复”。
- 验证: 列出执行过的命令及其结果,或者直接说明无法运行这些命令。
对于 Rust 代码本身的风格,如果已安装 skill,请遵循该 skill。
rust-best-practices