缘起

从 C++ 转 Rust,最不适应的不是语法,而是一种全新的思维模式

Rust 的所有权系统(Ownership)是语言最核心的创新,也是最陡峭的学习曲线。

C++ 的惯性思维

在 C++ 里,我们习惯了这样的写法:

std::string get_name() {
    return "BvBeJ";  // 编译器会处理返回值优化
}

void process() {
    std::string name = get_name();
    std::string alias = name;  // 拷贝?还是引用?
    // ...
}  // name 和 alias 都会析构

直觉告诉我们这里发生了拷贝。但在 Rust 里,同样的思维会让你碰壁。

Rust 的所有权规则

Rust 遵循三条简单规则:

  1. 每个值有一个所有者(Owner)
  2. 同一时间只有一个所有者
  3. 当所有者离开作用域,值被丢弃(Dropped)
fn main() {
    let s1 = String::from("hello");
    let s2 = s1;  // s1 被"移动"到 s2

    // println!("{}", s1);  // ❌ 编译错误!s1 已经无效
    println!("{}", s2);      // ✅
}  // s2 离开作用域,内存被释放

“移动"语义取代了 C++ 的拷贝——这是最大的思维转变。

借用:不用所有权的艺术

如果每次都要转移所有权,编程会非常麻烦。于是有了借用(Borrow):

fn calculate_length(s: &String) -> usize {
    s.len()
}  // s 离开作用域,但并不拥有所有权,所以不释放

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);  // 借用,而不是移动

    println!("'{}' 的长度是 {}", s1, len);  // ✅ s1 仍然有效
}

借用就像是指针,但更安全。Rust 编译器会确保:

  • 要么多个不可变引用 &T
  • 要么一个可变引用 &mut T
  • 二者不能同时存在

可变引用的风险

fn main() {
    let mut s = String::from("hello");

    let r1 = &s;      // ✅
    let r2 = &s;      // ✅
    println!("{} and {}", r1, r2);
    // r1 和 r2 在这里之后不再使用

    let r3 = &mut s;  // ✅ 没有问题,因为 r1, r2 已经不再使用
    r3.push_str(" world");
}

这叫非词法作用域生命周期(Non-Lexical Lifetimes, NLL),Rust 2018 edition 引入的优化。

实际例子:用 Rust 重写 C++ 的配置解析

// C++ 版本
class Config {
    std::string path_;
    std::unique_ptr<Parser> parser_;
public:
    Config(const std::string& path) : path_(path) {
        parser_ = std::make_unique<Parser>(path_);
    }
};

// Rust 版本
struct Config {
    path: String,
}

impl Config {
    fn new(path: &str) -> Result<Self, Box<dyn Error>> {
        let path = path.to_string();
        // 不需要 Box<dyn Parser>,直接在作用域内处理
        let _parser = Parser::new(&path)?;
        Ok(Config { path })
    }
}

不需要 Boxunique_ptr,Rust 编译器帮我们管理生命周期。

我的心得

C++Rust
手动管理 new/delete编译器推导生命周期
shared_ptr 到处飞借用检查器静态保证
悬垂指针是运行时bug编译时就能发现
析构顺序靠经验作用域决定析构

Rust 教会我一件事:很多运行时错误,可以在编译时避免。

代价是学习曲线陡峭。但一旦理解了这套系统,你会对内存管理有全新的认识。


下一篇文章聊聊 Rust 的 Trait 对象与动态分发。