mcpField
R
rust-port

rust-port

github.com/morganlinton开发1.4k198 次安装今天更新
代码仓库

关于 rust-port

rust-port 是帮助 Rust 开发者将现有程序移植为符合 Rust 惯例实现的技能,重点保持可观察行为而非照搬原有结构。它提供从行为分析、测试迁移到验证和编写移植说明的流程。

主要功能

  1. 01先梳理原程序的可观察行为
  2. 02先将原有测试迁移为 Rust 测试
  3. 03按 Rust 类型和错误模型设计 API
  4. 04针对 C、C++、Python、Go、JavaScript 和 TypeScript 的映射建议
  5. 05运行构建、测试与 Clippy 检查
  6. 06编写 API、测试及差异说明

适用场景

  1. 01将 C 或 C++ 库移植到 Rust
  2. 02在 Rust 中重写 Python 或 JavaScript 程序
  3. 03确保移植版本保留边界行为与结果顺序
  4. 04为代码审查准备移植记录

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/morganlinton/beautifulrust --skill rust-port

安装后在 Claude Code 中输入/rust-port确认已加载。

包含的文件

除 SKILL.md 外,安装时一起下载的参考资料和脚本。

evals/port-c-config-parser/
case.yaml
place-files.sh
prompt.md
evals/port-c-config-parser/files/
config.c
config.h
test_config.c
evals/port-c-config-parser/graders/
01-builds-and-tests-pass.md
02-clippy-clean.md
03-no-unsafe.md
04-every-test-ported.md
05-errors-as-result.md
06-error-implements-error.md
07-option-for-absent.md
08-typed-flags.md
09-behavior-preserved.md
10-porting-notes.md
11-proportionate.md
evals/port-python-task-graph/
case.yaml
evals/port-python-task-graph/files/
taskgraph.py
test_taskgraph.py
evals/port-python-task-graph/graders/
01-builds-and-tests-pass.md
02-clippy-clean.md
03-every-test-ported.md
04-exceptions-as-error-enum.md
05-kind-is-enum.md
06-priority-is-option.md
07-definition-order-preserved.md
08-order-and-cycle-match.md
09-summary-every-kind-in-order.md
10-porting-notes.md

详细介绍

Rust 移植

目标是写出 Rust 开发者会用 Rust 编写的代码来解决同一个问题,并保持相同的可观察行为。逐行转写(用 Rust 语法写 C,或用 Box<dyn Any> 写 Python)虽然能编译,却会保留原实现中的所有问题,还会额外带上借用检查器。移植行为,而不是结构。

1. 动笔前先理解原实现

  1. 阅读代码及其测试,以及所有调用它的代码(如果有)。
  2. 列出可观察行为:每个公共函数、它的输入和输出,以及所有可能的失败方式。也要包括那些容易被忽略的细节,因为移植最容易在这些地方出错:结果顺序、平局时如何处理、空输入时会发生什么、空白和大小写的处理方式、整数范围,以及哪些情况算错误、哪些是有效的边界情况。
  3. 标记哪些行为只是偶然实现细节,也就是原实现仅因其语言而采用的做法:手动内存管理、输出参数、返回 NULL、全局状态、定长缓冲区、仅用于归类函数的类,或 isinstance 检查。这些都不需要照搬。
  4. 如果原实现有缺陷或存在歧义,就在移植说明中标明并保留其行为,让用户决定。移植时不要悄悄修复问题。

2. 先移植测试

先将原测试移植为 Rust 测试,再移植实现。每个原测试对应一个 Rust 测试,并使用能对应回原测试的名称,以便读者检查覆盖情况。若原实现没有测试,则根据第 1 步列出的行为编写特征测试。

3. 先设计 Rust API,再实现

根据行为列表决定公共类型和签名,不要照搬原实现的形式。然后针对移植后的测试编写实现。

从 C 和 C++ 移植
原实现中的内容Rust 中的做法
malloc/free、new/delete、create/destroy 配对使用所有权类型(String、Vec、Box),它们会自动释放;其他情况使用 Drop
用 NULL 表示“缺失”Option<T>
错误码(-1、ERR_* 定义)、errno使用带错误枚举的 Result<T, E>,每个错误码对应一个变体,并实现 Display 和 std::error::Error
输出参数(int *err_line、long *out)使用返回值:将数据放入 Ok 值或错误变体中
标志位掩码(FLAG_A | FLAG_B)使用带命名字段和 Default 的选项结构体,或使用 bitflags crate;不要使用原始 u32
char * 加长度、strcmp、strcasecmp使用 &str/String、==、eq_ignore_ascii_case
指针加计数、按索引遍历使用切片、迭代器
定长缓冲区及其截断行为使用可增长类型;除非长度限制本身就是行为,否则移除限制
#define 常量和宏使用 const、枚举和函数
全局或静态可变状态使用由调用方持有的结构体
strtol 等函数使用 str::parse 和匹配的整数类型;将解析失败映射到错误枚举

除非移植必须通过 FFI 与 C 交互,否则不要编写 unsafe 或使用原始指针;如确有必要,请按照 rust-best-practices 技能中的说明将其限制在局部范围内。

从 Python 移植
原语言中的写法Rust 中的写法
异常层次结构使用一个错误枚举(或每个领域一个),为每个异常类设置一个变体;数据属性变为变体字段
None 作为值Option<T>
将字符串用作封闭集合(kind in ("build", "test"))使用枚举,并为文本形式实现 FromStr 和 Display
将字典或类用作记录使用带命名字段的结构体
dict 的迭代顺序(保证为插入顺序)当顺序可观察时,使用 Vec 或有序映射;普通 HashMap 会悄然改变输出顺序
鸭子类型、isinstanceTrait 或枚举
无界 int有意选择固定宽度的整数;输入可能很大时,处理溢出(checked_*)
sorted(..., key=...)sort_by_key 或 sort_by;与 Python 一样,两者都是稳定排序
str.splitlines()、strip()、isalnum()lines()、trim()、char::is_alphanumeric;应针对原实现检查 \r\n、Unicode 和空字符串等边界情况
围绕解析操作使用 try/except使用带错误映射的 ?,或对 Result 使用 match
从 Go、JavaScript 和 TypeScript 移植
  • Go 的 (value, error) 返回值变为 Result;nil 变为 Option;接口变为 trait;goroutine 和 channel 则根据原实现的需求,变为使用 std::sync::mpsc 的线程或异步任务。
  • JavaScript 的 null 和 undefined 变为 Option;抛出的错误变为 Result;普通 对象变为结构体;数字变为有意选择的固定宽度整数或 f64。

4. 保持移植规模适度

力求在所有可能的输入上都与原实现一致,通常不是正确的目标。 只需在实际会收到的输入上保持一致,其余情况使用 Rust 标准库即可。

  • 不要重新实现源语言的运行时。 使用 str::lines、str::trim、 str::parse 和 char 方法,而不要手写 Python 的 splitlines、strip、 int() 或 repr() 的副本,也不要手写 C 中依赖区域设置的 isspace 和 strtol。如果 Rust 的行为只在 Unicode 数字、非 ASCII 空白字符或无界整数等罕见输入上有所不同,应在移植说明中列出差异,而不是专门编写代码来处理。只有在用户说明会遇到这些输入时,才编写兼容层。
  • 不要扩大 API。 移植原实现的公开接口,并实现 Rust 调用方通常期望的标准 trait(集合的 Debug、Clone、Display、Error、FromStr、 Default 和 IntoIterator)。除非原实现的调用方需要,否则不要添加构建器、Index 实现、 FromIterator、Extend 和自定义迭代器类型。
  • 保留算法,除非它有问题。 只有为了解决实际问题(例如对真实输入的处理复杂度为二次方)时,才换用其他算法,并在说明中解释原因。
  • 检查规模。 统计代码行数,不包括测试、注释和文档注释。从 C 或 C++ 移植的代码通常与原实现规模相近。从 Python 或 JavaScript 移植的代码通常是原实现的两到三倍,因为 Rust 需要明确写出类型、错误枚举及其 trait 实现。规模大得多时,通常是上面三种习惯之一导致的。

与原实现进行差分测试值得一做,但几十个真实场景和边界情况的输入就足以回答问题。使用数百万个输入进行模糊测试,通常只会发现应记录在说明中的罕见差异。

5. 验证

如果有 Rust 工具链,请运行以下命令,并在报告前修复所有问题:

cargo build
cargo test
cargo clippy --all-targets -- -D warnings

所有已移植的测试都必须通过。如果也能运行原实现的测试,请一并运行,并使用相同输入比较结果。

6. 编写移植说明

在代码之外,编写一份简短的 PORTING.md,方便审阅者在几分钟内检查:

  • API 映射: 列出每个原始函数或类型及其对应的 Rust 实现的表格。
  • 测试映射: 列出每个原始测试及覆盖它的 Rust 测试。
  • 刻意做出的差异: 说明现在行为有所不同之处及原因,例如不再存在的内存不足错误、已取消的固定缓冲区限制,或 Rust 标准库对特殊输入的处理与原实现不同。
  • 保留的特性: 列出有意保留的奇怪行为,以免有人意外将其“修正”。
  • 验证: 列出运行过的命令及其结果;如果无法运行,请直接说明。

Rust 代码的风格请遵循 rust-best-practices skill(如果已安装)。

评论

支持 Markdown