Введение
Code review — неотъемлемая часть профессиональной разработки. В экосистеме Rust, где на первом месте стоит безопасность памяти и производительность, ревью кода приобретает особое значение. Неправильное использование unsafe, игнорирование правил владения (ownership) или неявные паники могут свести на нет все преимущества языка.
В этой статье мы разберем, на что обращать внимание при code review в Rust, как структурировать обратную связь и какие инструменты помогут автоматизировать проверки. Материал будет полезен как новичкам, так и опытным разработчикам, желающим упорядочить процесс ревью.
1. Безопасность: проверка unsafe кода и неявных паник
Rust гарантирует безопасность памяти, но unsafe — это отказ от этих гарантий. Каждый блок unsafe должен быть тщательно проверен.
Что проверять в unsafe коде?
- Корректность указателей: нет ли разыменования нулевого или висячего указателя.
- Выравнивание данных: правильное ли выравнивание при приведении сырых указателей.
- Алиасинг: нет ли нарушений правил алиасинга (например, одновременное изменяемое и неизменяемое заимствование).
- Инварианты: соблюдены ли инварианты структур данных (например, бинарная куча остается кучей).
// Плохой пример: небезопасное приведение без проверки выравнивания
fn bad_unsafe(data: &[u8]) -> &[u32] {
unsafe {
std::slice::from_raw_parts(
data.as_ptr() as *const u32,
data.len() / 4
)
}
}
// Хороший пример: с проверкой выравнивания
fn good_unsafe(data: &[u8]) -> Option<&[u32]> {
if data.len() % 4 != 0 || data.as_ptr() as usize % 4 != 0 {
return None;
}
Some(unsafe {
std::slice::from_raw_parts(
data.as_ptr() as *const u32,
data.len() / 4
)
})
}
Неявные паники
В Rust много операций, которые могут вызвать панику: unwrap(), expect(), panic!(), деление на ноль, выход за границы массива. На code review стоит обратить внимание:
- Используйте
unwrap()только в тестах или когда ошибка действительно невозможна. - Предпочитайте
unwrap_or(),unwrap_or_else()или обработку черезmatch. - Проверьте, что все
asпреобразования не теряют данные (например,u64 as u32).
// Плохо: неявная паника
let value = some_option.unwrap();
// Хорошо: явная обработка
let value = some_option.unwrap_or_else(|| {
log::error!("Missing expected value");
default_value()
});
2. Владение и заимствование: типичные ошибки
Система владения — главная особенность Rust. Ошибки компилятора здесь — лучшие друзья, но code review помогает найти логические ошибки, которые компилятор не ловит.
Избыточные клонирования
Частая проблема — клонирование там, где можно передать ссылку. Это снижает производительность и увеличивает использование памяти.
// Плохо: клонирование всей строки
fn process(data: Vec<String>) {
for item in data {
let cloned = item.clone();
do_something(&cloned);
}
}
// Хорошо: передача ссылки
fn process(data: &[String]) {
for item in data {
do_something(item);
}
}
Ненужные заимствования и lifetime
Проверьте, не слишком ли длинное время жизни у ссылок. Иногда можно упростить сигнатуру функции, убрав лишние аннотации lifetime.
// Сложно: избыточные lifetime
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
// Если входные данные не связаны, можно использовать разные lifetime
fn longest_improved<'a, 'b>(x: &'a str, y: &'b str) -> &'a str
where 'b: 'a {
if x.len() > y.len() { x } else { y }
}
3. Идиоматичность кода и использование инструментов
Rust имеет свой уникальный стиль. На code review проверяйте соответствие Rust Style Guide и использование стандартных идиом.
Идиомы Rust
- Использование
OptionиResult: избегайте булевых флагов, используйтеOptionдля опциональных значений. - Конструкции
if letиwhile let: они делают код более читаемым. - Цепочки итераторов: предпочитайте
filter().map().collect()вместо циклов с временными переменными.
// Неидиоматично: ручной цикл
let mut evens = Vec::new();
for x in numbers {
if x % 2 == 0 {
evens.push(x * 2);
}
}
// Идиоматично: цепочка итераторов
let evens: Vec<i32> = numbers
.iter()
.filter(|&&x| x % 2 == 0)
.map(|x| x * 2)
.collect();
Инструменты для автоматизации
Включите в CI следующие проверки — они сэкономят время на ревью:
- Clippy: линтер для Rust, ловит неидиоматичный код и потенциальные ошибки.
- Rustfmt: автоматическое форматирование кода.
- cargo deny: проверка лицензий и уязвимостей зависимостей.
- cargo audit: поиск известных уязвимостей.
# Пример конфигурации CI для GitHub Actions
- name: Run