Как правильно разбить модуль Rust на несколько файлов?
Основная идея: модуль = папка или файл
В Rust модуль может быть представлен либо отдельным файлом (shapes.rs), либо папкой с файлом mod.rs (или shapes/mod.rs в современном Rust — просто shapes.rs как корень модуля, а содержимое — в подфайлах). Ваша ошибка в том, что вы не реэкспортировали типы из подмодулей наверх. По умолчанию, если вы пишете pub mod rectangle; в mod.rs, то rectangle становится подмодулем shapes, но его содержимое не видно напрямую из main.rs как shapes::Rectangle — оно доступно только как shapes::rectangle::Rectangle.
Правильная структура
Создайте папку shapes/ и внутри неё следующие файлы:
src/
├── main.rs
└── shapes/
├── mod.rs // корень модуля shapes
├── rectangle.rs
└── circle.rs
shapes/rectangle.rs — содержимое такое же, как у вас было в shapes.rs:
pub struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
pub fn new(w: f64, h: f64) -> Rectangle {
Rectangle { width: w, height: h }
}
pub fn area(&self) -> f64 {
self.width * self.height
}
}
shapes/circle.rs:
pub struct Circle {
radius: f64,
}
impl Circle {
pub fn new(r: f64) -> Circle {
Circle { radius: r }
}
pub fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
}
shapes/mod.rs — ключевой файл. Здесь мы объявляем подмодули и реэкспортируем их публичные типы:
// Объявляем подмодули
pub mod rectangle;
pub mod circle;
// Реэкспортируем основные типы на уровень модуля shapes
pub use rectangle::Rectangle;
pub use circle::Circle;
Теперь в main.rs вы можете использовать:
mod shapes;
fn main() {
let rect = shapes::Rectangle::new(3.0, 4.0);
let circ = shapes::Circle::new(2.5);
println!("Rect area: {}, Circle area: {}", rect.area(), circ.area());
}
Почему это работает?
pub mod rectangle;— делает подмодульrectangleпубличным, но его содержимое доступно только по полному путиshapes::rectangle::Rectangle.pub use rectangle::Rectangle;— реэкспортирует типRectangleна уровень модуляshapes, так что он становится доступен какshapes::Rectangle.- Без
pub useвам пришлось бы писатьuse shapes::rectangle::Rectangle;вmain.rs, что неудобно.
Современный синтаксис (Rust 2018+)
Начиная с Rust 2018, вместо mod.rs можно использовать файл с тем же именем, что и модуль-папка. То есть вы можете переименовать shapes/mod.rs в shapes.rs, а подмодули оставить в папке shapes/:
src/
├── main.rs
├── shapes.rs // корень модуля (вместо mod.rs)
└── shapes/
├── rectangle.rs
└── circle.rs
Содержимое shapes.rs будет таким же, как было в mod.rs. Этот подход считается более чистым, так как файл корня модуля не называется одинаково для всех папок.
Дополнительный совет: префиксы и конфликты имён
Если вы реэкспортируете много типов, следите за возможными конфликтами имён. Например, если у вас есть Rectangle в двух подмодулях, придётся использовать алиасы: pub use rectangle::Rectangle as Rect;. В целом, реэкспорт — это стандартная практика для создания чистого API.
Альтернативный подход: один файл с вложенными модулями
Если ваша кодовая база не слишком большая, вы можете вообще не разбивать на отдельные файлы, а использовать вложенные модули прямо в одном файле. Например, в shapes.rs:
pub mod rectangle {
pub struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
pub fn new(w: f64, h: f64) -> Rectangle {
Rectangle { width: w, height: h }
}
pub fn area(&self) -> f64 {
self.width * self.height
}
}
}
pub mod circle {
pub struct Circle {
radius: f64,
}
impl Circle {
pub fn new(r: f64) -> Circle {
Circle { radius: r }
}
pub fn area(&self) -> f64 {
std::f64::consts::PI * self.radius * self.radius
}
}
}
// Реэкспорт для удобства
pub use rectangle::Rectangle;
pub use circle::Circle;
Этот способ проще для понимания новичками, но при росте проекта файл станет нечитаемым. Я рекомендую использовать его только для небольших модулей (до 200 строк). Для серьёзных проектов лучше сразу переходить на разбиение по файлам, как описано в основном ответе.
Ещё один вариант — использовать include! макрос для вставки содержимого других файлов, но это считается плохой практикой, так как нарушает систему модулей и видимости. Придерживайтесь стандартного подхода с mod и pub use.
Ответов пока нет.
Войдите чтобы оставить ответ
Блоги