Rust и командная работа: почему code style — это не про бюрократию
Когда вы пишете код на Rust в одиночку, вы — и архитектор, и ремесленник, и QA. Вы знаете, что означает переменная tmp и почему в модуле utils лежит функция do_stuff. Но как только к проекту подключается второй разработчик (или вы возвращаетесь к своему коду через полгода), магия заканчивается. Начинается хаос.
Работа в команде над проектами на Rust требует не только понимания borrow checker'а, но и строгих договорённостей о том, как писать код. Это не бюрократия — это способ сохранить рассудок и время. В этой статье мы разберём ключевые конвенции и code style для Rust, которые помогут вашей команде писать предсказуемый, читаемый и поддерживаемый код.
Rustfmt: ваш первый союзник в борьбе за единообразие
Самый простой способ внедрить code style в команде — использовать Rustfmt. Это официальный инструмент форматирования, который автоматически приводит код к единому стандарту. В Rust сообщество настолько серьёзно относится к форматированию, что rustfmt стал частью стандартного инструментария.
Как настроить Rustfmt в проекте
Создайте файл rustfmt.toml в корне проекта. Вот минимальная конфигурация, которая подходит для большинства команд:
# rustfmt.toml
max_width = 100
tab_spaces = 4
edition = "2021"
use_small_heuristics = "Max"
Почему это важно? Представьте, что два разработчика используют разные отступы или ширину строк. При слиянии веток Git будет показывать конфликты там, где их нет по сути. Rustfmt решает эту проблему раз и навсегда.
Совет для команды: добавьте проверку форматирования в CI/CD. Если код не соответствует rustfmt, пайплайн должен падать. Это жёстко, но эффективно.
Конвенции именования: как не запутаться в собственных названиях
Rust — строгий язык, и конвенции именования здесь чётко регламентированы. Их нарушение воспринимается сообществом как признак неопытности.
Основные правила именования
- Типы и трейты:
PascalCase—struct UserProfile,trait AsyncHandler - Функции, методы, переменные:
snake_case—fn parse_input(),let user_name - Константы и статические переменные:
SCREAMING_SNAKE_CASE—const MAX_RETRIES: u32 = 3 - Модули:
snake_case—mod database_connector - Типы-перечисления (variants):
PascalCase—enum Status { Active, Inactive }
Пример правильного и неправильного подхода:
// ❌ Плохо
struct userProfile {
name: String,
}
fn ParseData(input: &str) -> bool { false }
// ✅ Хорошо
struct UserProfile {
name: String,
}
fn parse_data(input: &str) -> bool { false }
Именование префиксов: зачем и когда
В Rust не принято использовать венгерскую нотацию или префиксы типа m_ для полей структур. Вместо этого используйте самодокументируемые названия. Если поле приватное, это обозначается модификатором pub, а не префиксом.
Документирование кода: пишем для людей, а не для компилятора
Rust обладает одной из лучших систем документирования среди всех языков. Документационные комментарии (три слеша ///) автоматически генерируют документацию через cargo doc. В команде это must-have.
Что должно быть в документации
- Публичные API: каждая функция, структура, трейт и метод с модификатором
pub - Примеры использования: Rust позволяет встраивать примеры кода, которые тестируются автоматически
- Предусловия и паники: укажите, при каких условиях функция может вызвать
panic!
Пример хорошо документированной функции:
/// Вычисляет факториал числа n.
///
/// # Arguments
/// * `n` - Неотрицательное целое число
///
/// # Panics
/// Паникует, если n > 20 (переполнение u64)
///
/// # Examples
/// ```
/// use my_crate::math::factorial;
/// assert_eq!(factorial(5), 120);
/// ```
pub fn factorial(n: u32) -> u64 {
(1..=n).product()
}
Конвенция: в команде договоритесь, что каждый PR должен включать документацию для новых публичных элементов. Это резко снижает количество вопросов в чате.
Обработка ошибок: единый стиль для всей команды
Rust предлагает два основных подхода к ошибкам: Result<T, E> и Option<T>. В командной работе важно выбрать единый стиль и придерживаться его.
Рекомендации по обработке ошибок
- Используйте кастомные типы ошибок через
thiserrorилиanyhow. Избегайте голых строк как ошибок. - Не злоупотребляйте
unwrap()— это источник паник. В production-коде используйтеexpect()с осмысленным сообщением. - Создайте единый модуль ошибок в проекте, например
errors.rs, где описаны все возможные ошибки.
Пример единого подхода:
// errors.rs
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AppError {
#[error("Database connection failed: {0}")]
DatabaseError(String),
#[error("Validation error: {0}")]
ValidationError(String),
#[error("Not found")]
NotFound,
}
// Использование
fn get_user(id: u32) -> Result<User, AppError> {
if id == 0 {
return Err(AppError::ValidationError("ID must be positive".into()));
}
// ...
}
Модульная структура проекта: как не утонуть в файлах
Когда проект растёт, организация модулей становится критичной. В Rust нет жёстких правил, но есть общепринятые конвенции.
Рекомендуемая структура
- Один файл — одна логическая единица. Не пишите гигантские файлы на 1000+ строк.
- Используйте
mod.rsилиlib.rsдля корневого модуля. Современный Rust (edition 2021) позволяет использоватьcrate::module_nameбезmod.rs.
<