Motivation
rust uses some file structure to organize its code into so-called modules. Additionally the mod
keyword can be used to define a certain structure within a file. And then … you basically put the pub
keyword in front of every item (i.e. struct
/fn
/…) and it becomes part of the public API. Now the question arises: Can one completely turn around the module structure of the public API without changing the internal module structure?
Example project
We consider this contrived example: Let a.rs
contain …
pub fn fn2() {
println!("fn2");
}
Let b.rs
contain …
pub fn fn3() {
println!("fn3");
}
pub mod c {
pub fn fn4() {
println!("fn4");
}
}
And finally, lib.rs
contains …
mod a;
mod b;
pub fn fn1() {
println!("fn1");
}
Then the internal API includes the following items (for rustaceans: consider each like prepended with use crate::
):
fn1
a::fn2
b::fn3
b::c::fn4
The current situation
-
The
mod
keywords inlib.rs
make the compiler consider the filesa.rs
andb.rs
. -
Since the
mod
keywords inlib.rs
are not prepended by `pub `, its elements are not exported. -
As a result, only
fn1
is part of the public API.
Our goal
We want to answer the question “Can the public API be completely turned around compared to the internal API?” As a study, we want to implement the following API:
fn4
c::fn1
c::a::fn2
c::a::b::fn3
Implementation
We can add the following lines to lib.rs
to achieve this structure:
pub use crate::b::c::fn4 as fn4;
pub mod c {
pub use crate::fn1;
pub mod a {
pub use crate::a::fn2 as fn2;
pub mod b {
pub use crate::b::fn3;
}
}
}
Conclusion
Can we completely turn around the public API compared to the internal API? Thus, can we handle the external structure independent of the internal structure? The answer is no. But only because of two minor reasons:
-
Because
pub
occurs inpub fn fn1
inlib.rs
, this item is exported in the public API as well (the spec says we only want to see it reexported in modulec
) -
This problem does not occur in our contrived example, but would occur if our top-level module is called
a
instead ofc
. Simply put, it is not allowed to havemod a;
andmod a {…}
statements in the same file. And there is no mechanism for renaming here to circumvent this problem.
If you can live with these restrictions (just put fn1
inside a private module in lib.rs
& don’t name your top-level public item the same like one of your private modules), then the answer is Yes. In general, the modules-inside-a-file mechanism provides a lot of flexibility. So you can easily start implementing and then adjust the public API easily right before publication.