Step 5 of 7
Re-exports with pub use
How you organize modules internally is your business. People using your code want short, stable paths. pub use brings a name into a module and makes it public there: a re-export.
mod shop {
mod catalog {
pub struct Product {
pub name: String,
pub cents: u32,
}
}
mod pricing {
use super::catalog::Product;
pub fn total(products: &[Product]) -> u32 {
products.iter().map(|p| p.cents).sum()
}
}
pub use catalog::Product;
pub use pricing::total;
}
use shop::{total, Product};
fn main() {
let cart = vec![
Product { name: String::from("rope"), cents: 1250 },
Product { name: String::from("lamp"), cents: 3999 },
];
println!("{} items, {} cents", cart.len(), total(&cart));
println!("first: {}", cart[0].name);
}
2 items, 5249 cents
first: rope
catalogandpricingare private modules: nobody outsideshopcan writeshop::catalog::Product.pub use catalog::Product;makesshop::Producta public path to the same struct. Users writeshop::Productand never learn thatcatalogexists. Later you can moveProductto another module, or splitcatalogin two, and nobody's code breaks.
The standard library does this all the time. std::collections::HashMap is really defined in a private module deep inside std and re-exported where you find it. Crates often offer a prelude module that re-exports their most-used items, so one glob brings them all in: use rayon::prelude::*;. Rust itself has a prelude: that's why Vec, String, Option and Some work without any use.
A plain use is private
A use without pub is a private shortcut for its own module. Others can't go through it:
mod shop {
mod catalog {
pub struct Product {
pub name: String,
}
}
use catalog::Product;
pub fn make() -> Product {
Product { name: String::from("rope") }
}
}
fn main() {
let p: shop::Product = shop::make();
println!("{}", p.name);
}
error[E0603]: struct import `Product` is private
Notice that shop::make() itself is fine: you can receive a Product without being able to name its type. Making it pub use catalog::Product; fixes the error.
Designing a library's public API
In a library crate, the paths users can reach are the API you promise to keep working. A common layout keeps the modules private and re-exports the important items at the top (in lib.rs), so users write mycrate::Product instead of mycrate::catalog::types::Product. pub use can also rename: pub use v2::parse as parse_v2;.
Your task
The tests use geometry::Circle and geometry::area, but those live in the private modules geometry::shapes and geometry::math. Add re-exports to geometry (don't move the code, and keep the inner modules private) so the tests compile and pass.
Challenge 2
Give the game module a prelude: a public module prelude inside game that re-exports Player and spawn, so that one line, use game::prelude::*;, brings in everything a user needs. The tests do exactly that.
Challenge 3
Your library has an old parser (v1::parse) and a new one (v2::parse, which also accepts spaces around the number and _ separators like 1_000). Users should call api::parse for the new one and api::parse_legacy for the old one. Inside mod api, add re-exports with renaming (pub use ... as ...) where needed. Don't copy the functions.
Previous: Struct fields, privacy and pub(crate) Next: Splitting a crate into files