Introduction: Why Code Review in Rust Is Not Just a Formality
\n\nCode review is one of the key processes in developing any high-quality software. But when it comes to Rust, this process takes on special significance. Rust is a language with a unique system of ownership and borrowing, which the compiler checks at compile time. However, even if the code compiles, it doesn't mean it is written well, safely, and efficiently.
\n\nIn this article, we will break down exactly what to pay attention to during code review of Rust projects, how to give constructive feedback without offending a colleague, and how to avoid common mistakes that the compiler doesn't catch. The article is aimed at developers who are already familiar with the basics of Rust but want to deepen their review skills.
\n\n1. Memory Safety and Resource Management
\n\nRust guarantees memory safety without a garbage collector. But this does not negate the need to check how the developer uses unsafe blocks, raw pointers, and external FFI calls.
What to check:
\n- \n
- Use of
unsafe: everyunsafeblock should be justified and accompanied by a comment explaining why it is safe. If you can do withoutunsafe, it is better to do so. \n - Resource leaks: check that all
Box,Rc,Arc, and other smart pointers are correctly freed. For example, cyclic references viaRcwithoutWeakare a common problem. \n - Drop order: if a struct contains fields with a custom
Dropimplementation, ensure that the destruction order does not lead to errors. \n
// Example of bad code: unsafe without comments\nunsafe {\n let ptr = ...;\n // No explanation of why this is safe\n *ptr = 42;\n}\n\n// Good code: comment and safe alternative\n// Use unsafe only to call a C function that guarantees pointer validity\nunsafe {\n // The pointer was obtained from a C API that guarantees its validity for the lifetime of the call\n some_c_function(ptr);\n}\n\n\n2. Code Idiomaticity and Style
\n\nRust has its own idioms that differ from C++ or Python. Code review should check how well the code aligns with the spirit of the language.
\n\nWhat to check:
\n- \n
- Use of
OptionandResult: avoidunwrap()andexpect()without an explicit reason. In production code, use error handling viamatch, the?operator, orunwrap_or_else. \n - Pattern matching: prefer
if letandmatchover chains ofis_some()/is_ok(). \n - Use of traits: check that traits are implemented meaningfully. For example, the
Displayimplementation should provide readable output, not justDebug. \n - Naming: in Rust, it is conventional to use
snake_casefor variables and functions,CamelCasefor types and traits. Constants useSCREAMING_SNAKE_CASE. \n
// Bad: excessive use of unwrap\nlet value = some_option.unwrap();\n\n// Good: error handling\nlet value = match some_option {\n Some(v) => v,\n None => return Err("Value not found".into()),\n};\n\n// Even better: with the ? operator\nlet value = some_option.ok_or("Value not found")?;\n\n\n3. Performance and Allocations
\n\nRust provides low-level control over memory, but this doesn't mean the code is automatically fast. During review, it is important to check for potential bottlenecks.
\n\nWhat to check:
\n- \n
- Unnecessary allocations: check whether
StringorVecare being created