Modules and Visibility
Importing with use
A use declaration creates a local shortcut (binding) in the current scope to a path in the module tree so you can refer to types, traits, functions, or modules by their short name instead of repeating the full path at every use site.
Idiomatic use Conventions by Item Kind
In Rust, how deeply you import a path depends on whether the target is a type/trait or a function:
| Item Kind | Idiomatic use Granularity |
Example use |
Call-Site Appearance | Why |
|---|---|---|---|---|
| Structs, Enums, Traits | Import the item itself | use std::collections::HashMap; |
let map = HashMap::new(); |
Type names are UpperCamelCase and self-descriptive |
| Functions | Import the parent module | use std::env; |
let args = env::args(); |
Shows that args() comes from env rather than being a local function |
| Enum Variants | Usually qualify with Enum::Variant, or import directly when unambiguous |
use Status::{Active, Idle}; |
Active |
Avoids repetition in large match blocks |
| Trait (for methods only) | use Trait as _; (anonymous import) |
use std::io::Write as _; |
buf.write_all(b"hi") |
Unlocks trait methods without polluting the type namespace with the name Write |
Grouping Imports with Nested Paths ({...}) and Glob Imports (*)
- Nested path lists (
use path::{A, B, sub::C};): Combine multiple imports sharing a common prefix into a single declaration. - Self-import in a group (
use std::io::{self, Read};): Imports bothstd::ioandstd::io::Read. - Glob import (
use path::*;): Brings all public items ofpathinto scope. Because*can make it unclear where a name was defined, reserve glob imports for test modules (use super::*;) and crate preludes (use my_crate::prelude::*;).
use std::collections::{BTreeSet, HashMap};
use std::fmt::{self, Write as _};
fn format_scores(scores: &HashMap<&str, u32>) -> Result<String, fmt::Error> {
let mut sorted_names: BTreeSet<&str> = scores.keys().copied().collect();
sorted_names.insert("bonus");
let mut out = String::new();
for name in sorted_names {
let val = scores.get(name).copied().unwrap_or(0);
// `.write_fmt` (via `write!`) is available because `std::fmt::Write as _` is in scope:
write!(&mut out, "{name}={val} ")?;
}
Ok(out)
}
fn main() {
let mut map = HashMap::new();
map.insert("alice", 95);
map.insert("bob", 88);
println!("{}", format_scores(&map).unwrap());
}
alice=95 bob=88 bonus=0
Scoping Rules for use Declarations
A use declaration is scoped to the module or block in which it appears:
- A
useat the top ofsrc/main.rsis in scope forsrc/main.rs, but is not automatically in scope inside a childmod sub { ... }unless that child module writes its ownuse(oruse super::*;). - A
usecan even be placed inside an individualfnbody to keep an import local to that single function:
#[derive(Debug)]
enum TrafficLight {
Red,
Yellow,
Green,
}
fn next_light(current: TrafficLight) -> TrafficLight {
// Scoped `use` inside a function body so variant names don't pollute the module:
use TrafficLight::{Green, Red, Yellow};
match current {
Red => Green,
Green => Yellow,
Yellow => Red,
}
}
fn main() {
let light = next_light(TrafficLight::Red);
println!("Next light: {light:?}");
}
Next light: Green