Rust代码审查:检查什么才能让代码不崩溃

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

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

开始课程

为什么Rust代码审查不仅仅是检查括号



Rust——一种承诺安全内存管理和高性能的语言。但即使是最严格的编译器也无法避免逻辑错误、非惯用代码或不合理的unsafe使用。Rust代码审查是一门艺术。你不仅仅是检查花括号是否闭合。你要检查开发者如何绕过(或未能绕过)借用检查器的限制,迭代器的使用效率如何,以及是否试图通过原始指针来“欺骗”系统。



在本文中,我将介绍在审查Rust代码时需要注意哪些方面。没有多余的理论——只有实用建议、示例和一份可以贴在墙上(或保存到书签)的检查清单。我们开始吧!



1. 所有权、借用和生命周期:痛苦的主要来源



首先需要检查的是所有权系统是否正确运行。即使代码能编译,也不意味着它是正确的。典型问题包括:



  • 过度克隆。当可以使用引用时,开发者却复制了数据。这会严重影响性能。
  • 可变和不可变借用。如果在同一作用域中同时存在&mut T和&T——就会出错。但有时可以通过重新排列代码来避免这种情况。
  • 不正确的生命周期。特别是在包含多个引用的复杂结构中。


示例:糟糕的代码

fn process(data: &Vec<i32>) -> i32 {    let mut sum = 0;    for &x in data.iter() {        sum += x;    }    // 没问题,但为什么要传递 &Vec,如果可以用 &[i32]?    sum}


好的代码

fn process(data: &[i32]) -> i32 {    data.iter().sum()}


审查时检查什么:是否传递了&Vec、&String或&HashMap,而实际上使用切片就足够了?是否不必要地使用了clone()?生命周期注解是否正确标注?



2. Unsafe:红色区域



unsafe块就像安全Rust中的敞开的活板门。它们必须有充分的理由、最小化并仔细记录。在代码审查中要注意:



  • 为什么使用unsafe?通常可以使用安全抽象来替代。
  • 原始指针的正确性。是否解引用了空指针?内存释放后引用是否仍然存在?
  • 不变量。包含unsafe的代码必须明确描述它维护了哪些不变量。


示例:危险的unsafe

unsafe fn dangerous() {    let ptr = 0x12345 as *mut i32;    *ptr = 42; // 如果这个地址什么都没有呢?}


这样的代码不应该通过审查。即使“它能工作”——这也不是理由。



好的unsafe

unsafe fn init_buffer(buf: &mut [u8]) {    // 我们确信buf指向有效内存,    // 因为调用方保证了这一点。    std::ptr::write_bytes(buf.as_mut_ptr(), 0, buf.len());}


检查什么:每个unsafe是否有注释说明理由?是否有测试来检查边界情况?是否违反了安全保证?



3. 闭包和函数式风格



Rust鼓励函数式方法:迭代器、闭包、map、filter、fold。但这可能导致过度复杂化。在审查时要注意:



  • 是否过度使用闭包?如果闭包捕获了太多变量,也许应该将逻辑提取到单独的函数中。

博客

书籍推荐