Модульная система Rust на самом деле невероятно гибка и позволит вам раскрыть любую структуру, которую вы хотите, скрывая, как ваш код структурирован в файлах.
Я думаю, что ключевым моментом здесь является использование pub use
, которое позволит вам повторно экспортировать идентификаторы из других модулей. Прецедент этого есть в std::io
ящике Rust, где некоторые типы из подмодулей реэкспортируются для использования вstd::io
.
Изменить (2019-08-25): следующая часть ответа была написана довольно давно. Он объясняет, как настроить такую модульную структуру в rustc
одиночку. Сегодня Cargo обычно используется в большинстве случаев. Хотя нижеследующее все еще в силе, некоторые его части (например #![crate_type = ...]
) могут показаться странными. Это не рекомендуемое решение.
Чтобы адаптировать ваш пример, мы могли бы начать с этой структуры каталогов:
src/
lib.rs
vector.rs
main.rs
Вот ваш main.rs
:
extern crate math;
use math::vector;
fn main() {
println!("{:?}", vector::VectorA::new());
println!("{:?}", vector::VectorB::new());
}
И ваш src/lib.rs
:
#[crate_id = "math"];
#[crate_type = "lib"];
pub mod vector; // exports the module defined in vector.rs
И наконец src/vector.rs
:
// exports identifiers from private sub-modules in the current
// module namespace
pub use self::vector_a::VectorA;
pub use self::vector_b::VectorB;
mod vector_b; // private sub-module defined in vector_b.rs
mod vector_a { // private sub-module defined in place
#[derive(Debug)]
pub struct VectorA {
xs: Vec<i64>,
}
impl VectorA {
pub fn new() -> VectorA {
VectorA { xs: vec![] }
}
}
}
И здесь происходит волшебство. Мы определили подмодуль, math::vector::vector_a
который имеет некоторую реализацию особого вида вектора. Но мы не хотим, чтобы клиенты вашей библиотеки заботились о наличии vector_a
подмодуля. Вместо этого мы хотели бы сделать его доступным в math::vector
модуле. Это делается с помощью pub use self::vector_a::VectorA
, который повторно экспортирует vector_a::VectorA
идентификатор в текущий модуль.
Но вы спросили, как это сделать, чтобы можно было поместить свои специальные векторные реализации в разные файлы. Это то, что mod vector_b;
делает линия. Он указывает компилятору Rust искать vector_b.rs
файл для реализации этого модуля. И, конечно же, вот наш src/vector_b.rs
файл:
#[derive(Debug)]
pub struct VectorB {
xs: Vec<i64>,
}
impl VectorB {
pub fn new() -> VectorB {
VectorB { xs: vec![] }
}
}
С точки зрения клиента, тот факт, что VectorA
и VectorB
определены в двух разных модулях в двух разных файлах, совершенно непрозрачен.
Если вы находитесь в том же каталоге, что и main.rs
, вы сможете запустить его с помощью:
rustc src/lib.rs
rustc -L . main.rs
./main
В целом, глава «Ящики и модули» в книге Rust довольно хороша. Примеров много.
Наконец, компилятор Rust также автоматически просматривает подкаталоги. Например, приведенный выше код будет работать без изменений с этой структурой каталогов:
src/
lib.rs
vector/
mod.rs
vector_b.rs
main.rs
Команды для компиляции и запуска также остаются прежними.