初级 Rust 开发者常见错误:进阶篇

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

初级 Rust 开发者常见错误:进阶篇



Rust 是一种承诺内存安全和高性能的语言。然而,当你从基础示例转向实际项目时,一些无法通过简单添加 & 或 mut 解决的错误开始浮现。本文将剖析四个进阶陷阱,即使是已经掌握 borrow checker 和所有权基础的开发者也常会陷入其中。



本文面向那些已经用 Rust 写过几个项目,并接触过 Rc、RefCell、trait 和 unsafe 的读者。我们不会解释什么是借用,而是专注于那些隐藏的坑。



1. Rc/RefCell 循环引用导致的内存泄漏



最隐蔽的错误是认为 Rc 会自动防止内存泄漏。实际上,Rc 使用引用计数,如果两个对象相互引用,计数器永远不会归零,内存也不会被释放。



来看一个经典的图结构示例:



use std::rc::Rc;use std::cell::RefCell;

#[derive(Debug)]struct Node { value: i32, children: Vec<Rc<RefCell<Node>>>,}

fn main() { let a = Rc::new(RefCell::new(Node { value: 1, children: vec![] })); let b = Rc::new(RefCell::new(Node { value: 2, children: vec![] }));

// 创建循环引用 a.borrow_mut().children.push(Rc::clone(&b)); b.borrow_mut().children.push(Rc::clone(&a));

// 退出 main 后内存不会被释放! drop(a); drop(b); // 泄漏:Rc 认为仍有引用存在}


为什么会这样? 在 drop(a) 之后,a 的引用计数减少,但 b 仍然通过 children 持有对 a 的引用。同理 b 也是如此。最终两个对象都留在内存中。



解决方案: 使用 Weak<T> 作为弱引用。在图结构中,通常一个节点是所有者(强引用),其余为弱引用。



use std::rc::{Rc, Weak};use std::cell::RefCell;

#[derive(Debug)]struct Node { value: i32, parent: RefCell<Weak<Node>>, // 指向父节点的弱引用 children: RefCell<Vec<Rc<Node>>>, // 指向子节点的强引用}

fn main() { let leaf = Rc::new(Node { value: 3, parent: RefCell::new(Weak::new()), children: RefCell::new(vec![]), });

let branch = Rc::new(Node { value: 5, parent: RefCell::new(Weak::new()), children: RefCell::new(vec![Rc::clone(&leaf)]), });

*leaf.parent.borrow_mut() = Rc::downgrade(&branch); // 现在当 branch 被删除时,leaf 也会被释放}


2. 自引用结构中 Pin 的错误使用



包含指向自身引用的结构体(自引用结构)是导致未定义行为的常见原因。Rust 不允许直接创建它们,但通过 Pin 和 unsafe 可以实现。常见错误是忘记 Pin 仅保证对象在内存中不会被移动,但无法防止内部偏移。



以 future 为例:



use std::pin::Pin;use std::marker::PhantomPinned;

#[derive(Debug)]struct SelfReferential { data: String, pointer: *const String, // 指向 data 的裸指针 _pin: PhantomPinned,}

impl SelfReferential { fn new(data: String) -> Self { SelfReferential { pointer: std::ptr::null(), data, _pin: PhantomPinned, } }

fn init(self: Pin<&mut Self>) { let this = unsafe { self.get_unchecked_mut() }; this.pointer = &this.data as *const String; }}

博客

书籍推荐