一个Rust Unicode Bug,三家编码模型实测对比

用同一个 Rust Unicode 切片 Bug 和固定提示词,对比三家编码模型的补丁质量与可复现评估方法。
文章以一个按字节切片的 Rust 函数为测试题:它在处理 `café` 等合法 UTF-8 输入时会于字符中间切断而 panic。作者用完全相同的提示词、温度 0 和回归测试,比较 DeepSeek V4 Pro、Claude Fable 5.1 与 Grok Build 0.1 的首次响应。评估不看解释的自信程度,而看 `cargo test`、编译输出、token 用量、耗时和模型版本,并要求 API 标识符对照在线目录核实。正确修复需用 `char_indices` 找到第 n 个标量值后的字节边界并保留 `&str` 返回类型。文章强调单个 Bug 不能给模型排名,最好的编码 LLM 取决于具体任务。
一个Rust Unicode Bug,三家编码模型实测对比

编码模型的排行榜很难说明它处理真实 Bug 的能力。一个在 ASCII 上运行良好的 Rust 函数,可能因为切错了 UTF-8 字节边界,而在合法的 Unicode 输入上直接 panic。本文用一个可复现的 Bug、一条固定提示词和同一套回归测试,比较 DeepSeek广告 V4 Pro、Claude Fable 5.1 与 Grok Build 0.1 三个模型给出的补丁质量——看代码,而不是看解释的自信程度。
这个 Bug 为什么值得拿来测模型
被测函数的目标是返回字符串的前 n 个字符:
pub fn prefix(input: &str, n: usize) -> &str {
if n >= input.len() {
return input;
}
&input[..n]
}
问题在于它按字节计数,而不是按字符计数。UTF-8 中 café 末尾的 é 占两个字节,当 n=4 时,切片恰好从该字符中间切开,触发 panic。作为对照的测试用例如下:
#[test]
fn ascii() { assert_eq!(prefix("rustacean", 4), "rust"); }
#[test]
fn unicode() { assert_eq!(prefix("café", 4), "café"); }
#[test]
fn beyond_length() { assert_eq!(prefix("é", 10), "é"); }
将代码保存为新建 Cargo library 项目的 src/lib.rs,执行 cargo test 即可复现 Unicode 用例的失败。这个 Bug 的好处是边界清晰:修复必须保留 &str 返回类型、尊重字符边界、并通过回归测试,任何走捷径的改动都会在编译器或测试里暴露出来。
UTF-8 是一种变长编码:ASCII 字符占 1 字节,拉丁扩展字符(如 é)占 2 字节,CJK 字符通常占 3 字节,emoji 多占 4 字节。Rust 的字符串切片 &input[..n] 按字节索引,若 n 落在某个多字节字符的中间,就会切出一个非法的 UTF-8 序列。Rust 在运行时检测到这一点会直接 panic,而不是返回乱码,这正是该 Bug 如此显眼的原因。理解这一点需要区分三个层次:字节(byte)、Unicode 标量值(scalar value,即一个 char)和字素簇(grapheme cluster,用户感知的一个字符)。例如 é 可以是单个标量值 U+00E9,也可以由 e 加组合重音 U+0301 两个标量值组成,但在字素簇层面它们都显示为一个字符。本题要求按标量值计数,因此用 char_indices 而非按字素簇切分也是正确的。
三家模型,同一道题
本次比较的三个候选是 DeepSeek V4 Pro、Claude Fable 5.1 和 Grok Build 0.1。原文特别强调,模型可用性、确切的 API 标识符和受支持的参数,在真正执行前必须对照在线目录核实,不能凭记忆填写。
| 模型 | API 标识符 | 温度 | 输入 | 版本 |
|---|---|---|---|---|
| DeepSeek V4 Pro | 从目录确认 | 0 | 同一 Rust 文件与提示词 | 记录 API 版本 |
| Claude Fable 5.1 | 从目录确认 | 0 | 同一 Rust 文件与提示词 | 记录 API 版本 |
| Grok Build 0.1 | 从目录确认 | 0 | 同一 Rust 文件与提示词 | 记录 API 版本 |
所有模型使用相同的请求体、系统指令、最大输出额度和测试环境,并记录 API 实际返回的模型 ID。温度设为 0 能降低随机性,但原文也提醒:它并不保证重复调用得到完全一致的答案。这一点对“可复现对比”的严谨性很关键。
提示词:一字不改
每个模型收到的提示词完全相同:
Fix the Rust function below. It must return the first n Unicode scalar values without panicking. Keep the return type &str. Explain the bug briefly, return a complete src/lib.rs, and add a regression test for an emoji and a combining character. Do not add external dependencies.
提示词之后附上完整的原始 src/lib.rs。规则同样明确:不要把某个模型的失败告诉另一个模型,也不要悄悄修好某个回答再去测试。这保证了比较的公平性——模型拿到的是同一道题,而不是被“喂”过答案的题。
每个模型的补丁该看什么
DeepSeek V4 Pro
需要捕获原始响应、报告的输入输出 token 数、请求耗时,以及第一次 cargo test 的输出。正确的补丁应当遍历 char 边界,而不是在任意字节索引处切片。如果返回的代码无法编译,保留错误信息,并把下一次提示算作另一次尝试。
Claude Fable 5.1
使用完全相同的源码和提示词。重点记录它是否保留了借用的 &str 返回类型,是否处理了请求长度超过字符数的情况。组合字符用例要特别留意:Unicode 标量值不等于字素簇,对一个 e 加组合重音请求一个标量值,可能只返回 e——按本题定义这是正确的。
Unicode 标量值(Unicode scalar value)指除代理对范围 U+D800–U+DFFF 之外的所有码点,Rust 的 char 类型恰好对应一个标量值。字素簇(grapheme cluster)则是人类感知的最小书写单位,可能由多个标量值组合而成,例如 e 加组合尖音符、家庭 emoji 由多个码点通过零宽连接符拼接。题目明确要求返回前 n 个标量值,因此像 e 加组合重音的输入,请求 1 个标量值时返回 e 是符合题意的;若模型返回整个字素簇反而偏离了要求。这一区分是判断模型是否真正理解题意、而非笼统地“处理 Unicode”的关键。
Grok Build 0.1
重复相同流程,不修改提示词。审查模型是否改动了 API、引入了依赖,或返回 String 而非 &str。即使可见输出看起来正确,这些改动同样重要。首次尝试和后续尝试要分开记录。
在评估补丁时,cargo test 只验证行为,cargo fmt --check 和 cargo clippy 则分别检查格式一致性与常见缺陷。clippy 是 Rust 官方提供的静态分析工具,能捕获如不必要的分配、可疑的类型转换、低效的迭代等模式,对编码模型而言,它常能暴露“能跑但不够地道”的写法。而 cargo fmt --check 不修改文件,只报告格式差异,适合在自动化流程中作为门禁。将三者组合使用,可以区分“编译通过且测试通过”“风格不达标”“存在 lint 警告”等不同质量层次,避免仅凭一次 cargo test 绿色就高估模型输出。
正确的修复长什么样
一种有效做法是用 char_indices 找到第 n 个标量值之后的字节边界:
pub fn prefix(input: &str, n: usize) -> &str {
let end = input
.char_indices()
.nth(n)
.map(|(index, _)| index)
.unwrap_or(input.len());
&input[..end]
}
需要说明的是,这段参考实现是判断正确性的“标准答案”,并非任何模型的输出,而且它必须被排除在模型提示词之外。测试用例应覆盖 ASCII、多字节字符、空字符串、n 等于 0、n 大于字符数、emoji 以及分解重音等情形。
记分卡:先填表,再谈排名
| 模型 | 首次通过 | 尝试次数 | 耗时 | 输入 token | 输出 token | 备注 |
|---|---|---|---|---|---|---|
| DeepSeek V4 Pro | 来自 cargo test | 来自日志 | 来自日志 | 来自 API | 来自 API | 编译与行为备注 |
| Claude Fable 5.1 | 来自 cargo test | 来自日志 | 来自日志 | 来自 API | 来自 API | 编译与行为备注 |
| Grok Build 0.1 | 来自 cargo test | 来自日志 | 来自日志 | 来自 API | 来自 API | 编译与行为备注 |
原文对此说得很直白:只有当每一行都由首次响应、Cargo 输出和 API 日志填充后,记分卡才有意义;在那之前,它只是一张可重复执行的评估工作表,而不是模型排名。
不同编码任务该怎么选模型
短补丁场景下,首次编译通过和边界用例正确比解释是否漂亮更重要。大型重构时,应观察模型改动无关代码的频率。代码审查场景,可以测试它能否在无提示的情况下识别“字节 vs 字符”的区别。终端自动化场景,则比较各模型对所需输出格式的遵循程度是否稳定。
原文的结论是:最好的编码 LLM 取决于任务。一个 Rust Bug 无法断定哪个模型在整仓库、整语言或整工具链上最强,但它能揭示在受控条件下,模型是否处理了某个具体的失败模式。
如何复现这次比较
用 cargo new unicode-prefix --lib 创建 Cargo library,把 src/lib.rs 替换为有 Bug 的版本并运行 cargo test。将相同的提示词和文件发送给每个已确认的模型标识符,保存原始 JSON 响应、请求时间戳、token 用量字段和模型版本。每次尝试都把候选代码提取到干净的工作树中,运行 cargo fmt --check、cargo test 和 cargo clippy,并记录失败而不修改首次回答。
仓库建议按如下结构组织:
baseline/src/lib.rs
oracle/src/lib.rs
prompts/task.txt
runs/<model>/response.json
runs/<model>/cargo-test.txt
可复现的产物是代码和日志,而不是一张排名图。一份可分享的对比应当包含仓库、未经编辑的首次响应和 Cargo 日志,让读者既能检查候选修复,也能看到失败。
相关推荐

Orca多Agent编排实战:Claude与Codex并行协作指南
Orca是Stable AI开源的免费Agent工作台,支持Claude Code、Codex、Kimi等34个agent并行运行。本文介绍其Git worktree隔离机制、多模型编排分工方案(强模型拆解+便宜模型编码)、手机远程管理功能及实操上手步骤,帮助你高效管理多个AI编程助手。

思维链不止属于AI:人类如何用CoT提升思考质量
思维链(Chain of Thought)不只是大模型的提示词技巧。本文从Hacker News讨论出发,分析CoT为何对人类思考同样有效,拆解可操作的迁移方法,并指出人类CoT与机器CoT的关键差异。
Cloudflare联手OpenAI:用AI打通漏洞发现与防御闭环
Cloudflare联手OpenAI:用AI打通漏洞发现与防御闭环
Cloudflare 使用 OpenAI Daybreak 调查安全漏洞并生成可评审的修复方案,开发者同时借助 Codex 提升编码效率与代码质量,探索漏洞发现到防御响应的一体化路径。