patternrustCritical
Split a module across several files
Viewed 0 times
moduleseveralacrossfilessplit
Problem
I want to have a module with multiple structs in it, each in its own file. Using a
I want each struct to be in the same module, which I would use from my main file, like so:
However Rust's module system (which is a bit confusing to begin with) does not provide an obvious way to do this. It seems to only allow you to have your entire module in one file. Is this un-rustic? If not, how do I do this?
Math module as an example:Math/
Vector.rs
Matrix.rs
Complex.rs
I want each struct to be in the same module, which I would use from my main file, like so:
use Math::Vector;
fn main() {
// ...
}However Rust's module system (which is a bit confusing to begin with) does not provide an obvious way to do this. It seems to only allow you to have your entire module in one file. Is this un-rustic? If not, how do I do this?
Solution
Rust's module system is actually incredibly flexible and will let you expose whatever kind of structure you want while hiding how your code is structured in files.
I think the key here is to make use of
Edit (2019-08-25): the following part of the answer was written quite some time ago. It explains how to setup such a module structure with
To adapt your example, we could start with this directory structure:
Here's your
And your
And finally,
And this is where the magic happens. We've defined a sub-module
But you asked how to do this so that you could put your special vector implementations in different files. This is what the
From the client's perspective, the fact that
If you're in the same directory as
In general, the "Crates and Modules" chapter in the Rust book is pretty good. There are lots of examples.
Finally, the Rust compiler also looks in sub-directories for you automatically. For example, the above code will work unchanged with this directory structure:
The commands to compile and run remain the same as well.
I think the key here is to make use of
pub use, which will allow you to re-export identifiers from other modules. There is precedent for this in Rust's std::io crate where some types from sub-modules are re-exported for use in std::io.Edit (2019-08-25): the following part of the answer was written quite some time ago. It explains how to setup such a module structure with
rustc alone. Today, one would usually use Cargo for most use cases. While the following is still valid, some parts of it (e.g. #![crate_type = ...]) might seem strange. This is not the recommended solution.To adapt your example, we could start with this directory structure:
src/
lib.rs
vector.rs
main.rsHere's your
main.rs:extern crate math;
use math::vector;
fn main() {
println!("{:?}", vector::VectorA::new());
println!("{:?}", vector::VectorB::new());
}And your
src/lib.rs:#[crate_id = "math"];
#[crate_type = "lib"];
pub mod vector; // exports the module defined in vector.rsAnd finally,
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,
}
impl VectorA {
pub fn new() -> VectorA {
VectorA { xs: vec![] }
}
}
}And this is where the magic happens. We've defined a sub-module
math::vector::vector_a which has some implementation of a special kind of vector. But we don't want clients of your library to care that there is a vector_a sub-module. Instead, we'd like to make it available in the math::vector module. This is done with pub use self::vector_a::VectorA, which re-exports the vector_a::VectorA identifier in the current module.But you asked how to do this so that you could put your special vector implementations in different files. This is what the
mod vector_b; line does. It instructs the Rust compiler to look for a vector_b.rs file for the implementation of that module. And sure enough, here's our src/vector_b.rs file:#[derive(Debug)]
pub struct VectorB {
xs: Vec,
}
impl VectorB {
pub fn new() -> VectorB {
VectorB { xs: vec![] }
}
}From the client's perspective, the fact that
VectorA and VectorB are defined in two different modules in two different files is completely opaque.If you're in the same directory as
main.rs, you should be able to run it with:rustc src/lib.rs
rustc -L . main.rs
./mainIn general, the "Crates and Modules" chapter in the Rust book is pretty good. There are lots of examples.
Finally, the Rust compiler also looks in sub-directories for you automatically. For example, the above code will work unchanged with this directory structure:
src/
lib.rs
vector/
mod.rs
vector_b.rs
main.rsThe commands to compile and run remain the same as well.
Code Snippets
src/
lib.rs
vector.rs
main.rsextern crate math;
use math::vector;
fn main() {
println!("{:?}", vector::VectorA::new());
println!("{:?}", vector::VectorB::new());
}#[crate_id = "math"];
#[crate_type = "lib"];
pub mod vector; // exports the module defined in 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![] }
}
}
}#[derive(Debug)]
pub struct VectorB {
xs: Vec<i64>,
}
impl VectorB {
pub fn new() -> VectorB {
VectorB { xs: vec![] }
}
}Context
Stack Overflow Q#22596920, score: 143
Revisions (0)
No revisions yet.