# 实现层面:所有权、错误处理与代码细节 提炼自指南的 Ownership and borrowing / Implementation concerns / Use the right per-codebase test framework 章节。 ## 所有权与借用 ### 避免 `Rc` / `Arc` 优先唯一所有权与借用。Rust 通常能不靠共享所有权指针表达对象图;这些指针有运行时开销和自身复杂性,从其他语言来的工程师容易过快使用。 例外:替代的所有权与借用结构复杂到难以编写、理解或维护(review 时判断)。 ### 借用优先于深拷贝 `.clone()` 只在真正需要深拷贝时用,不当借用的替代品;让代码顺应 Rust 的所有权与借用规则来设计。借用关系过分复杂时,clone 是务实选择;选择时同时考虑性能需求。 注:clone `Rc`/`Arc` 指针不是深拷贝,是其正常用法。 ## 不要过度依赖类型推断 写到"熟悉代码库的人能推断出类型"的程度。类型加在变量声明上,而不是加 turbofish;若编译器逼着写 turbofish,用 `_` 占位通常可以。 应加显式类型的场景:调用泛型返回类型的方法(`.into()`、`.parse()`);复杂迭代器链的结果。 ## 模式匹配优于 check-then-unwrap 输入数据保证不会 panic 时,用模式匹配向编译器证明正确性: ```rust // 好:match 向编译器证明了 x 是 Some,显然不会 panic if let Some(value) = option { use_value(value); } // 不好:unwrap 的正确性依赖运行时检查 if option.is_some() { use_value(option.unwrap()); } ``` ### `expect` 优于 `unwrap` 类型系统表达不了的不变量必须 panic 时,用 `expect` 记录理由。 ## 匹配 `#[non_exhaustive]` enum 时列出模式 尽量在 match 中列出全部变体;不关心的变体归入通配臂(`Shape::Triangle | _ => {}`);只关心特定变体时用 `if let` 或 `let … else`。例外:只关心特定变体但 match 比 `if let` 更干净时。 ## 用上下文字符串说清断言 `assert!` 的可选格式串用于说明断言为何应当成立、或为何被违反。特别值得加上下文的场景:本想加注释时(格式串的好处是会进日志);失败信息会到用户手里时;记录某个值有助于理解失败时;区分"调用方误用 API"与"实现有 bug"时。 ```rust // 解释断言为何成立 assert!( values.is_sorted(), "`values` is populated from a `PriorityQueue`, so we expect it to be sorted" ); // 记录值 assert!(refund > 0, "Refund must be positive, got {refund:?}"); // 标记 API 误用 assert!(self.stream.is_some(), "Call connect() before executing queries"); // 不好:只复述条件 assert!(values.is_sorted(), "values should always be sorted"); ``` ## 迭代器组合器链 vs 直接迭代 - 避免长而复杂的组合器链(尤其含条件、改外部状态的)。 - 不要在 `for` 头部构造链:先赋给具名变量再迭代——减少杂乱,变量名还能说明链的用途。 - 避免使用 `Iterator::for_each`(确有理由除外),`for` 循环通常更好读。 - 短链、组合器本身承载大量逻辑时组合器更优:`values.iter().any(|&v| v > threshold)` 好于手写 flag + break。 ```rust // 不好:for 头部构造链 for bug in bugs.iter().filter_map(|d| parse(d).ok()).filter(|b| b.priority <= Priority::P1) { … } // 好:具名变量 let urgent_bugs = bugs .iter() .filter_map(|d| parse(d).ok()) .filter(|b| b.priority <= Priority::P1); for bug in urgent_bugs { … } ``` ```rust // 不好:链里夹条件与外部状态 let _processed: Vec<_> = items .into_iter() .map(transform_that_may_fail) .map(|result| { if result.is_err() { error_count += 1; } result }) .filter_map(Result::ok) .collect(); // 好:对应的 for 循环 let mut processed = Vec::with_capacity(items.len()); for item in items { match transform_that_may_fail(item) { Ok(val) => processed.push(val), Err(_) => error_count += 1, } } ``` ## 禁用 `panic::catch_unwind` 第一方 Rust 代码禁用。例外:仅作纵深防御且绝不依赖其任何性质(代码库须已尽力避免 panic),且仅在可能以 `panic=unwind` 运行的环境中适用(Chrome、Android、Fuchsia 均要求 `panic=abort`,不适用)。 ## 拼接与替换用 `format!` / `write!` - 拼接超过两段字符串:`format!("{a}{b}{c}")` 而非 `a + b + c`。 - 对字面量做替换:用格式串而非 `str::replace`。 理由:比 `+` 拼接分配更少;标准格式串优于自造模板语法。 ## 测试框架 多数 Google 代码库用各自测试框架而非 `#[test]`:Chrome 用 rust_gtest_interop(Rust 测试并入 C++ 测试二进制);Fuchsia 用 `#[fuchsia::test]`。 (注:通用 Rust 项目照常 `#[test]` + `cargo test` 即可——此句为使用本技能时的补充说明,非指南原文。)