System modułów Rusta jest w rzeczywistości niesamowicie elastyczny i pozwoli Ci ujawnić dowolną strukturę, ukrywając strukturę kodu w plikach.
Myślę, że kluczem jest tutaj wykorzystanie pub use
, które pozwoli na reeksportowanie identyfikatorów z innych modułów. Jest to precedens w std::io
skrzynce Rusta, gdzie niektóre typy z podmodułów są ponownie eksportowane do użytku wstd::io
.
Edycja (25.08.2019): poniższa część odpowiedzi została napisana dość dawno temu. Wyjaśnia, jak skonfigurować taką strukturę modułu rustc
samodzielnie. Obecnie w większości przypadków używa się Cargo. Chociaż poniższe informacje są nadal aktualne, niektóre z nich (np. #![crate_type = ...]
) Mogą wydawać się dziwne. To nie jest zalecane rozwiązanie.
Aby dostosować Twój przykład, możemy zacząć od tej struktury katalogów:
src/
lib.rs
vector.rs
main.rs
Oto twoje main.rs
:
extern crate math;
use math::vector;
fn main() {
println!("{:?}", vector::VectorA::new());
println!("{:?}", vector::VectorB::new());
}
I twój src/lib.rs
:
#[crate_id = "math"];
#[crate_type = "lib"];
pub mod vector; // exports the module defined in vector.rs
I wreszcie 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![] }
}
}
}
I tu dzieje się magia. Zdefiniowaliśmy podmoduł, math::vector::vector_a
który ma pewną implementację specjalnego rodzaju wektora. Ale nie chcemy, aby klienci Twojej biblioteki przejmowali się tym, że istnieje vector_a
podmoduł. Zamiast tego chcielibyśmy udostępnić go w math::vector
module. Odbywa się to za pomocą pub use self::vector_a::VectorA
, który ponownie eksportuje vector_a::VectorA
identyfikator w bieżącym module.
Ale zapytałeś, jak to zrobić, aby móc umieścić swoje specjalne implementacje wektorowe w różnych plikach. To właśnie mod vector_b;
robi linia. Instruuje kompilator Rusta, aby szukał vector_b.rs
pliku do implementacji tego modułu. I rzeczywiście, oto nasz src/vector_b.rs
plik:
#[derive(Debug)]
pub struct VectorB {
xs: Vec<i64>,
}
impl VectorB {
pub fn new() -> VectorB {
VectorB { xs: vec![] }
}
}
Z punktu widzenia klienta fakt, że VectorA
i VectorB
są zdefiniowane w dwóch różnych modułach w dwóch różnych plikach, jest całkowicie nieprzejrzysty.
Jeśli jesteś w tym samym katalogu main.rs
, powinieneś móc go uruchomić za pomocą:
rustc src/lib.rs
rustc -L . main.rs
./main
Ogólnie rozdział „Skrzynie i moduły” w książce Rust jest całkiem niezły. Jest wiele przykładów.
Wreszcie, kompilator Rust automatycznie szuka również podkatalogów. Na przykład powyższy kod będzie działał bez zmian w tej strukturze katalogów:
src/
lib.rs
vector/
mod.rs
vector_b.rs
main.rs
Polecenia kompilacji i uruchomienia również pozostają takie same.