Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rust Best Practices Summary
Rust 最佳实践总结

What you’ll learn: Practical guidelines for writing idiomatic Rust, including code organization, naming, error handling, memory usage, performance habits, and which common traits are worth implementing.
本章将学到什么: 编写惯用 Rust 的一组实用准则,包括代码组织、命名、错误处理、内存使用、性能习惯,以及哪些常见 trait 值得实现。

Code Organization
代码组织

  • Prefer small functions: they are easier to test and reason about.
    优先写小函数:更容易测试,也更容易推理。
  • Use descriptive names: calculate_total_price() beats calc() every day.
    名字要说明白calculate_total_price() 远比 calc() 强。
  • Group related functionality: use modules and separate files to表达职责边界。
    把相关功能放在一起:用模块和拆文件表达清楚职责边界。
  • Write documentation: public API 就老老实实写 /// 文档。
    写文档:公开 API 就别偷懒,老老实实写 ///

Error Handling
错误处理

  • Avoid unwrap() unless the operation is truly infallible.
    除非真的是不可能失败,否则别乱用 unwrap()
#![allow(unused)]
fn main() {
// Bad: can panic
let value = some_option.unwrap();

// Better: handle the missing case
let value = some_option.unwrap_or(default_value);
let value = some_option.unwrap_or_else(|| expensive_computation());
let value = some_option.unwrap_or_default();

// For Result<T, E>
let value = some_result.unwrap_or(fallback_value);
let value = some_result.unwrap_or_else(|err| {
    eprintln!("Error occurred: {err}");
    default_value
});
}
  • Use expect() with a descriptive message when an unwrap-style failure would indicate a violated invariant.
    如果失败意味着不变量被破坏,就改用 expect() 并写清楚原因。
#![allow(unused)]
fn main() {
let config = std::env::var("CONFIG_PATH")
    .expect("CONFIG_PATH environment variable must be set");
}
  • Return Result<T, E> for fallible operations so callers decide what recovery means.
    可失败操作就返回 Result<T, E>,把恢复策略交给调用方。
  • Use thiserror for custom error types instead of手写一堆样板实现。
    自定义错误类型优先用 thiserror,别手搓一堆样板代码。
#![allow(unused)]
fn main() {
use thiserror::Error;

#[derive(Error, Debug)]
pub enum MyError {
    #[error("IO error: {0}")]
    Io(#[from] std::io::Error),
    
    #[error("Parse error: {message}")]
    Parse { message: String },
    
    #[error("Value {value} is out of range")]
    OutOfRange { value: i32 },
}
}
  • Use ? to propagate errors through the call stack cleanly.
    ? 传播错误,让调用链保持干净。
  • Prefer thiserror over anyhow for libraries and production code, because explicit error enums remain matchable by callers.
    库代码和正式生产代码里更推荐 thiserror 而不是 anyhow,因为显式错误枚举还能被调用方精确匹配。
  • Acceptable uses of unwrap():
    unwrap() 勉强算合理的场景:
    • Unit tests
      单元测试里
    • Short-lived prototypes
      短命原型代码里
    • Situations where failure has already been logically ruled out
      前面已经在逻辑上排除了失败的情况
#![allow(unused)]
fn main() {
let numbers = vec![1, 2, 3];
let first = numbers.get(0).unwrap();

let first = numbers.get(0)
    .expect("numbers vec is non-empty by construction");
}
  • Fail fast: validate preconditions early and bail out immediately when they do not hold.
    尽早失败:前置条件尽早检查,不成立就立刻返回错误。

Memory Management
内存管理

  • Prefer borrowing over cloning whenever ownership transfer is unnecessary.
    能借用就借用,别动不动就 clone。
  • Use Rc<T> sparingly and only when shared ownership is genuinely needed.
    Rc<T> 少用,只有真的需要共享所有权时再上。
  • Limit lifetimes with scopes: {} blocks can make drop timing explicit.
    用作用域控制生命周期:必要时直接上 {} 缩短值的存活时间。
  • Avoid exposing RefCell<T> in public APIs: keep interior mutability tucked inside implementations.
    别在公共 API 里乱暴露 RefCell<T>,内部可变性尽量藏在实现细节里。

Performance
性能

  • Profile before optimizing: use benchmarks and profiler data, not直觉表演。
    优化前先测:靠 benchmark 和 profiler 说话,别光靠直觉演戏。
  • Prefer iterators over manual loops when they improve clarity and allow optimization.
    优先考虑迭代器,写法更清晰时通常也更容易被优化。
  • Use &str instead of String whenever ownership is unnecessary.
    不需要所有权时就用 &str,别硬上 String
  • Move huge stack objects to the heap with Box<T> when needed.
    超大的栈对象必要时用 Box<T> 挪到堆上。

Essential Traits to Implement
值得考虑实现的核心 trait

Core Traits Every Type Should Consider
每个类型都该想一想的核心 trait

When building custom types, the goal is to make them feel native in Rust. These traits are the usual starting set.
自定义类型想写得像“原生 Rust 类型”,最先该考虑的通常就是下面这些 trait。

Debug and Display
DebugDisplay

#![allow(unused)]
fn main() {
use std::fmt;

#[derive(Debug)]
struct Person {
    name: String,
    age: u32,
}

impl fmt::Display for Person {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} (age {})", self.name, self.age)
    }
}

let person = Person { name: "Alice".to_string(), age: 30 };
println!("{:?}", person);
println!("{}", person);
}

Clone and Copy
CloneCopy

#![allow(unused)]
fn main() {
#[derive(Debug, Clone, Copy)]
struct Point {
    x: i32,
    y: i32,
}

#[derive(Debug, Clone)]
struct Person {
    name: String,
    age: u32,
}

let p1 = Point { x: 1, y: 2 };
let p2 = p1;

let person1 = Person { name: "Bob".to_string(), age: 25 };
let person2 = person1.clone();
}

PartialEq and Eq
PartialEqEq

#![allow(unused)]
fn main() {
#[derive(Debug, PartialEq, Eq)]
struct UserId(u64);

#[derive(Debug, PartialEq)]
struct Temperature {
    celsius: f64,
}

let id1 = UserId(123);
let id2 = UserId(123);
assert_eq!(id1, id2);
}

PartialOrd and Ord
PartialOrdOrd

#![allow(unused)]
fn main() {
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
struct Priority(u8);

let high = Priority(1);
let low = Priority(10);
assert!(high < low);

let mut priorities = vec![Priority(5), Priority(1), Priority(8)];
priorities.sort();
}

Default
Default

#![allow(unused)]
fn main() {
#[derive(Debug, Default)]
struct Config {
    debug: bool,
    max_connections: u32,
    timeout: Option<u64>,
}

impl Default for Config {
    fn default() -> Self {
        Config {
            debug: false,
            max_connections: 100,
            timeout: Some(30),
        }
    }
}

let config = Config::default();
let config = Config { debug: true, ..Default::default() };
}

From and Into
FromInto

#![allow(unused)]
fn main() {
struct UserId(u64);
struct UserName(String);

impl From<u64> for UserId {
    fn from(id: u64) -> Self {
        UserId(id)
    }
}

impl From<String> for UserName {
    fn from(name: String) -> Self {
        UserName(name)
    }
}

impl From<&str> for UserName {
    fn from(name: &str) -> Self {
        UserName(name.to_string())
    }
}
}

TryFrom and TryInto
TryFromTryInto

#![allow(unused)]
fn main() {
use std::convert::TryFrom;

struct PositiveNumber(u32);

#[derive(Debug)]
struct NegativeNumberError;

impl TryFrom<i32> for PositiveNumber {
    type Error = NegativeNumberError;
    
    fn try_from(value: i32) -> Result<Self, Self::Error> {
        if value >= 0 {
            Ok(PositiveNumber(value as u32))
        } else {
            Err(NegativeNumberError)
        }
    }
}
}

Serde for Serialization
序列化用的 Serde

#![allow(unused)]
fn main() {
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct User {
    id: u64,
    name: String,
    email: String,
}
}

Trait Implementation Checklist
trait 实现检查清单

#![allow(unused)]
fn main() {
#[derive(
    Debug,
    Clone,
    PartialEq,
    Eq,
    PartialOrd,
    Ord,
    Hash,
    Default,
)]
struct MyType {
    // fields...
}

impl Display for MyType { /* user-facing representation */ }
impl From<OtherType> for MyType { /* convenient conversion */ }
impl TryFrom<FallibleType> for MyType { /* fallible conversion */ }
}

When NOT to Implement Traits
什么时候不要乱实现 trait

  • Do not implement Copy for heap-owning types such as StringVecHashMap
    带堆数据的类型别实现 Copy,像 StringVecHashMap 都不合适。
  • Do not implement Eq for values that may contain NaN.
    可能含 NaN 的类型别实现 Eq
  • Do not implement Default when no sensible default exists.
    如果根本不存在“合理默认值”,就别硬实现 Default
  • Do not implement Clone casually for huge data structures if the cost is misleadingly high.
    巨大数据结构别随手实现 Clone,否则别人一用就可能踩性能雷。

Summary: Trait Benefits
trait 带来的直接好处

TraitBenefit
好处
When to Use
适用时机
Debugprintln!("{:?}", value)Almost always
几乎总该有
Displayprintln!("{}", value)User-facing types
面向用户展示的类型
Clonevalue.clone()Explicit duplication makes sense
明确复制有意义时
CopyImplicit duplicationSmall, plain-value types
小而简单的值类型
PartialEq== and !=Most comparable types
大多数可比较类型
EqReflexive equalityEquality is mathematically sound
相等关系严格成立时
PartialOrd<, >, <=, >=Naturally ordered types
存在自然顺序的类型
Ordsort(), BinaryHeapTotal ordering exists
存在全序关系时
HashHashMap keysAs map/set keys
要作为键使用时
DefaultDefault::default()Obvious default value exists
存在自然默认值时
From/IntoConvenient conversionsCommon conversions
存在常用转换时
TryFrom/TryIntoFallible conversionsConversion may fail
转换本来就可能失败时