Importing Standard Library Items
While Rust's prelude automatically brings the most common types (Option, Result, Vec, String) into scope, the rest of the standard library (std) is organized into modules such as std::collections, std::io, std::fs, std::cmp, and std::mem. You can refer to these items either by their full module path or by bringing their names into local scope with a use declaration.
Full Path vs use Declaration
Every item in the standard library has an unambiguous path starting with std:::
use std::collections::HashMap;
fn main() {
// 1. Using the short name `HashMap` brought into scope by `use`:
let mut scores = HashMap::new();
scores.insert("Blue", 10);
// 2. Using a full `std::...` path directly without a `use` statement:
let minimum = std::cmp::min(10, 25);
println!("scores = {scores:?}, minimum = {minimum}");
}
scores = {"Blue": 10}, minimum = 10
A use declaration has zero runtime cost; it merely creates a lexical alias in the current module scope so you do not have to repeat std::collections:: on every line.
Commonly Imported Standard Library Modules
| Module Path | Key Types, Traits, and Functions |
|---|---|
std::collections |
HashMap, HashSet, BTreeMap, BTreeSet, VecDeque, BinaryHeap |
std::cmp |
min, max, Ordering (Less, Equal, Greater), Reverse |
std::mem |
size_of, size_of_val, align_of, swap, replace, take |
std::fmt |
Display, Debug, Formatter, Result, Write |
std::io |
stdin, stdout, Read, Write, BufRead, BufReader, Error |
std::fs |
File, read_to_string, write, create_dir_all, read_dir |
std::path |
Path, PathBuf |
std::env |
args, var, current_dir |
std::time |
Duration, Instant, SystemTime |
std::sync |
Arc, Mutex, RwLock, mpsc, OnceLock |
std::thread |
spawn, sleep, current |
use Syntax Variations
Rust provides several ways to group, alias, and scope use imports:
| Syntax | Example | Effect |
|---|---|---|
| Single item import | use std::collections::HashMap; |
Brings HashMap into scope |
Nested paths ({...}) |
use std::cmp::{min, max, Ordering}; |
Imports multiple items from the same prefix in one line |
self in nested path |
use std::io::{self, Write}; |
Imports both std::io (as io) and std::io::Write (as Write) |
Aliasing with as |
use std::io::Result as IoResult; |
Imports the item under a different local name to avoid name collisions |
Glob import (*) |
use std::io::prelude::*; |
Imports all public items from the target module |
Local block use |
fn f() { use std::cmp::max; ... } |
Confines the import strictly to the enclosing block or function |
Combining self and Nested Paths
When you want to call module-qualified functions like io::stdin() while also bringing a trait like io::Write into scope directly, combine self inside braces:
use std::collections::{BTreeMap, HashMap};
use std::mem::{self, size_of};
fn main() {
let mut a = 1;
let mut b = 2;
// `mem` is in scope via `self`, and `size_of` is in scope directly:
mem::swap(&mut a, &mut b);
let _hash: HashMap<&str, i32> = HashMap::new();
let _tree: BTreeMap<&str, i32> = BTreeMap::new();
println!("a = {a}, b = {b}, size of i32 = {} bytes", size_of::<i32>());
}
a = 2, b = 1, size of i32 = 4 bytes
Resolving Name Collisions with Parent Modules or as
Both std::fmt and std::io define a type named Result. Importing both use std::fmt::Result; and use std::io::Result; in the same scope causes a duplicate-definition error (E0252).
Resolve collisions by either importing the parent modules (use std::fmt; use std::io; and writing fmt::Result / io::Result) or renaming one import with as:
use std::fmt::Result as FmtResult;
use std::io::Result as IoResult;
fn check_types() -> (FmtResult, IoResult<()>) {
(Ok(()), Ok(()))
}
fn main() {
println!("{:?}", check_types());
}
(Ok(()), Ok(()))
Importing Traits to Unlock Their Methods
In Rust, you can only call a trait's methods on a type if that trait is currently in scope! For example, String implements std::fmt::Write (allowing write!(&mut my_string, ...)), and std::io::Stdout implements std::io::Write (allowing .flush()), but neither trait is in the default prelude.
If you call a trait method without importing the trait, rustc tells you the exact use statement to add:
// ❌ does not compile
fn main() {
let mut buffer = String::new();
write!(&mut buffer, "Value: {}", 42).unwrap();
}
error[E0599]: cannot write into `String`
--> src/main.rs:3:5
|
3 | write!(&mut buffer, "Value: {}", 42).unwrap();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: `String` implements `std::fmt::Write`, which is required by `write!`, but it must be in scope
help: trait `Write` which provides `write_fmt` is implemented but not in scope; perhaps you want to import it
|
1 + use std::fmt::Write;
|
error: aborting due to 1 previous error
Adding use std::fmt::Write; brings the trait into scope and unlocks write! on String:
use std::fmt::Write;
fn main() {
let mut buffer = String::new();
write!(&mut buffer, "Value: {}", 42).unwrap();
println!("{buffer}");
}
Value: 42
Tip: If you want to import a trait solely for its methods without polluting your namespace with the trait's name (such as
Write), you can import it anonymously as_:use std::fmt::Write as _;.