Creating Idiomatic use Paths
In Listing 7-11, you might have wondered why we specified use crate::front_of_house::hosting
and then called hosting::add_to_waitlist
in eat_at_restaurant
, rather than specifying the use
path all the way out to the add_to_waitlist
function to achieve the same result, as in Listing 7-13.
Filename: src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}
use crate::front_of_house::hosting::add_to_waitlist;
pub fn eat_at_restaurant() {
add_to_waitlist();
}
Listing 7-13: Bringing the add_to_waitlist
function into scope with use
, which is unidiomatic
Although both Listing 7-11 and Listing 7-13 accomplish the same task, Listing 7-11 is the idiomatic way to bring a function into scope with use
. Bringing the function's parent module into scope with use
means we have to specify the parent module when calling the function. Specifying the parent module when calling the function makes it clear that the function isn't locally defined while still minimizing repetition of the full path. The code in Listing 7-13 is unclear as to where add_to_waitlist
is defined.
On the other hand, when bringing in structs, enums, and other items with use
, it's idiomatic to specify the full path. Listing 7-14 shows the idiomatic way to bring the standard library's HashMap
struct into the scope of a binary crate.
Filename: src/main.rs
use std::collections::HashMap;
fn main() {
let mut map = HashMap::new();
map.insert(1, 2);
}
Listing 7-14: Bringing HashMap
into scope in an idiomatic way
There's no strong reason behind this idiom: it's just the convention that has emerged, and folks have gotten used to reading and writing Rust code this way.
The exception to this idiom is if we're bringing two items with the same name into scope with use
statements, because Rust doesn't allow that. Listing 7-15 shows how to bring two Result
types into scope that have the same name but different parent modules, and how to refer to them.
Filename: src/lib.rs
use std::fmt;
use std::io;
fn function1() -> fmt::Result {
--snip--
}
fn function2() -> io::Result<()> {
--snip--
}
Listing 7-15: Bringing two types with the same name into the same scope requires using their parent modules.
As you can see, using the parent modules distinguishes the two Result
types. If instead we specified use std::fmt::Result
and use std::io::Result
, we'd have two Result
types in the same scope, and Rust wouldn't know which one we meant when we used Result
.