Bringing names in with use¶
Level: 101 → 201 · for newcomers
One line: use is a shortcut, not an import — it binds a name in this module, and deleting it removes nothing from the program except your ability to write the short spelling.
use std::collections::HashMap;
fn main() {
let mut t: HashMap<&str, u32> = HashMap::new();
t.insert("Ada", 3);
println!("{}", t["Ada"]); // 3
}
Nothing is loaded, compiled or linked by that line. Delete it and the program still works if you write std::collections::HashMap in full. This is the single most useful thing to know about use, because it is what separates it from Python's import and Java's — both of which people arrive expecting.
The four forms¶
| Form | Does |
|---|---|
use a::b::C; |
binds C |
use a::b::C as D; |
binds D — the fix for a collision |
use a::b::{C, D}; |
binds both; {self, C} also binds b |
use a::b::*; |
binds everything public in b |
as is not decoration. Two traits named Write in one file is E0252, "the name Write is defined multiple times", and rustc offers the fix itself: "you can use as to change the binding name of the import".
Importing enum variants¶
use election::Method::{self, Approval, Star};
fn describe(m: Method) -> &'static str {
match m {
Star => "score then automatic runoff",
Approval => "approve as many as you like",
Method::Plurality => "pick one",
}
}
Bare Star reads well inside a match on one enum and badly in a file with three enums that each have one. Some, None, Ok and Err are in the prelude for exactly this reason — they are common enough that the ambiguity never arises.
The trap: two globs, and an error that arrives later¶
That compiles. It is the use of a bare SEATS that fails, with E0659: "SEATS is ambiguous", "ambiguous because of multiple glob imports of a name in the same module".
So a glob sits there harmlessly for months and then breaks on the day somebody adds a name upstream — in a file you did not touch, in a build you did not expect. And even when it works, a reader cannot tell where a bare describe() came from without grepping both modules.
Two places a glob is right, and they share one property — there is exactly one place the names can have come from:
use my_crate::prelude::*— a module that exists to be glob-imported.use super::*at the top of a#[cfg(test)]module — the source is the file you are already reading, and the tests want its private items too.
What is in scope with no use at all¶
Vec, String, Option, Result, Box, Some, None, Ok, Err and the traits listed in std::prelude ↗ are injected into every module. That list is the reason a first Rust program has no imports at all, and the reason the first use line you write is usually std::collections::something.
If you are coming from another language¶
- Python. This is the difference that catches everybody:
importexecutes a module, andusedoes not — there is no import side effect, no import order, no circular-import failure, and no cost.from x import yisuse x::y,import x as zisuse x as z, andfrom x import *is the glob with the same reputation. Python's__init__.pyre-exporting names ispub use, which this page's neighbour covers. One genuine trap for Python programmers: auseinside a function is legal and scoped to that function, which looks like Python's local import but is doing something completely different — it is still just a name binding, not deferred loading. - ABAP. There is no import statement at all: every global class and interface is visible everywhere by its full name, which is why ABAP names carry their namespace (
/PARKER/CL_AR_INTERFACE) instead.useis closest to theALIASESstatement inside a class — a shorter local name for something whose real name is long — and theasrename is exactlyALIASES ... FOR .... What has no ABAP counterpart is the ambiguity: two global classes cannot share a name, so the collision this page is about is prevented by the naming convention rather than resolved by the language. - Java / C#.
importandusingare the same "shorten a name" mechanism, and Java's single-type-import versus on-demandimport x.*is exactly the named-versus-glob split, with the same advice. C#'susing X = Y;alias is Rust'sas. - C++.
usingdeclarations andusing namespace, with the same warning about the latter, and for the same reason.
The verified output¶
Verified output of the_use_declaration.rs — regenerated by tools/run_examples.py, never hand-typed.
1. `use` binds a name; the item was already there
`use std::collections::HashMap as Table` -> Table::new() = {"Ada": 3}
Deleting the `use` line does not remove HashMap from the program.
It only means you have to spell std::collections::HashMap in full.
Nothing is compiled, loaded or linked by a `use`.
2. `as` renames, and is the fix for a collision
`use std::fmt::Write` and `use std::io::Write` in one file is
E0252: "the name `Write` is defined multiple times". Rename one.
with fmt::Write in scope as FmtWrite: "12 ballots"
rustc even offers the fix: "you can use `as` to change the
binding name of the import". Note what this `use` bought — a
TRAIT in scope is what makes `write!` on a String resolve at all,
even though the name FmtWrite never appears again.
3. Braces bring several names from one path
use election::ballots::{count, spoiled};
count() = 12, spoiled() = 1
`{self, …}` also brings the module itself, so you can write both
`ballots::count()` and `count()`.
4. Importing enum variants, and when not to
use election::Method::{self, Approval, Star};
describe(Star) = score then automatic runoff
describe(Approval) = approve as many as you like
describe(Method::Plurality) = pick one
Bare `Star` reads well inside a match on one enum, and badly in a
file with three enums that each have a `Star`. `Option`'s Some and
None are in the prelude for exactly this reason — they are common
enough that the ambiguity never arises.
5. The glob, and why it is rare
`use election::Method::*;` compiles and is discouraged: a new
variant upstream can silently shadow a local name, and a reader
cannot tell where a bare `Star` came from. The two accepted uses
are a prelude module (`use my_crate::prelude::*`) and the inside
of a test module (`use super::*`), where the source is obvious.
6. What is already in scope without any `use` at all
Vec, String, Option, Result, Box, Some, None, Ok, Err and the
traits listed in std::prelude are injected into every module.
Everything else needs a path or a use: {"Ada": 3} needed one.
Practice¶
Two traits called Write, and a glob that shadows. Write a file that needs both std::fmt::Write and std::io::Write — one to write! into a String, one to write bytes into a Vec<u8> — and get it to compile. Note which of the two names you actually use afterwards, and what that says about why the use was needed at all.
Then glob-import two modules that both define SEATS. Predict whether the file compiles before the name is used, and after. Then say which two situations make a glob acceptable, and what those two have in common.
Solution
the_use_declaration_kata.rs in full — pasted here by tools/run_examples.py from the file CI compiles and runs.
//! Kata solution: two Writes, a glob that shadows, and `use super::*`.
//!
//! rustc --edition 2024 the_use_declaration_kata.rs -o /tmp/udk && /tmp/udk
use std::fmt::Write as FmtWrite;
use std::io::Write as IoWrite;
mod election {
#[derive(Debug, PartialEq)]
pub enum Method {
Star,
Approval,
}
pub const SEATS: u32 = 3;
pub fn describe() -> &'static str {
"one race"
}
}
mod other {
/// A name that collides with election::SEATS, on purpose.
pub const SEATS: u32 = 99;
/// And one that does not, so the glob below is genuinely in use.
pub fn tiebreak() -> &'static str {
"lot"
}
}
// Both globs. The compiler does NOT reject this; it defers.
use election::*;
use other::*;
fn main() {
println!("1. Two traits called Write, both needed");
let mut text = String::new();
write!(text, "{} seats", election::SEATS).unwrap();
let mut bytes: Vec<u8> = Vec::new();
IoWrite::write_all(&mut bytes, text.as_bytes()).unwrap();
println!(" fmt::Write on a String : {text:?}");
println!(" io::Write on a Vec<u8> : {:?}", String::from_utf8(bytes).unwrap());
println!(" Without the `as` renames this file is E0252, \"the name `Write` is");
println!(" defined multiple times\". rustc even writes the fix for you:");
println!(" \"you can use `as` to change the binding name of the import\".");
println!(" Note that FmtWrite is never NAMED below — bringing the trait into");
println!(" scope is the entire job, because `write!` on a String needs it.");
println!();
println!("2. Two globs, one ambiguous name");
println!(" `use election::*;` and `use other::*;` both bring in SEATS.");
println!(" This compiles. Writing a bare `SEATS` does not:");
println!(" E0659: \"`SEATS` is ambiguous\", \"ambiguous because of multiple");
println!(" glob imports of a name in the same module\".");
println!(" election::SEATS = {}, other::SEATS = {}", election::SEATS, other::SEATS);
println!(" The error arrives at the USE SITE, not at the import — so a glob");
println!(" can sit there harmlessly for months and then break the day");
println!(" somebody adds a name upstream.");
println!();
println!("3. What the glob did bring in unambiguously");
println!(" describe() = {} <- from election, via the glob", describe());
println!(" tiebreak() = {} <- from other, via the other glob", tiebreak());
println!(" Method::Star == Method::Approval: {}", Method::Star == Method::Approval);
println!(" Which is the glob's actual cost: a reader cannot tell where");
println!(" `describe` or `Method` came from without grepping both modules.");
println!();
println!("4. The two places a glob is fine");
println!(" a. `use my_crate::prelude::*` — a module that exists to be");
println!(" glob-imported, whose contents are a documented API.");
println!(" b. `use super::*` at the top of a #[cfg(test)] module — the");
println!(" source is the file you are already reading, and the tests");
println!(" want everything in it, private items included.");
println!(" Both share one property: there is exactly one place the names");
println!(" can have come from.");
}
Verified output of the_use_declaration_kata.rs — regenerated by tools/run_examples.py, never hand-typed.
1. Two traits called Write, both needed
fmt::Write on a String : "3 seats"
io::Write on a Vec<u8> : "3 seats"
Without the `as` renames this file is E0252, "the name `Write` is
defined multiple times". rustc even writes the fix for you:
"you can use `as` to change the binding name of the import".
Note that FmtWrite is never NAMED below — bringing the trait into
scope is the entire job, because `write!` on a String needs it.
2. Two globs, one ambiguous name
`use election::*;` and `use other::*;` both bring in SEATS.
This compiles. Writing a bare `SEATS` does not:
E0659: "`SEATS` is ambiguous", "ambiguous because of multiple
glob imports of a name in the same module".
election::SEATS = 3, other::SEATS = 99
The error arrives at the USE SITE, not at the import — so a glob
can sit there harmlessly for months and then break the day
somebody adds a name upstream.
3. What the glob did bring in unambiguously
describe() = one race <- from election, via the glob
tiebreak() = lot <- from other, via the other glob
Method::Star == Method::Approval: false
Which is the glob's actual cost: a reader cannot tell where
`describe` or `Method` came from without grepping both modules.
4. The two places a glob is fine
a. `use my_crate::prelude::*` — a module that exists to be
glob-imported, whose contents are a documented API.
b. `use super::*` at the top of a #[cfg(test)] module — the
source is the file you are already reading, and the tests
want everything in it, private items included.
Both share one property: there is exactly one place the names
can have come from.
See also¶
- A trait must be in scope — the
usewhose absence produces a method not found error on a method that exists - Modules and visibility — what the path has to get through
- One module per file — where the paths come from
- What an enum is — the variants imported above
Sources¶
The use declaration ↗ in Rust by Example, and the Reference's use declarations ↗. Both error transcripts were produced by compiling the collision.
Po polsku¶
use to skrót nazwy, a nie import — i to jest jedyna rzecz, którą naprawdę trzeba o nim wiedzieć. Linia use std::collections::HashMap; niczego nie wczytuje, nie kompiluje i nie linkuje; wiąże tylko nazwę HashMap w bieżącym module. Skasuj ją, a program działa dalej, pod warunkiem że wszędzie wypiszesz std::collections::HashMap w całości. Dlatego słowo „import” jest tu mylącym skrótem myślowym: w Pythonie import wykonuje moduł (są skutki uboczne, jest kolejność, są importy cykliczne), a use w Ruscie nie robi nic poza wprowadzeniem nazwy do zasięgu.
as nie jest ozdobnikiem, tylko lekarstwem na kolizję: dwie cechy (traits) o nazwie Write w jednym pliku to E0252 — „the name Write is defined multiple times” — a rustc sam dopisuje rozwiązanie: „you can use as to change the binding name of the import”. Warto przy okazji zauważyć rzecz, którą łatwo przeoczyć w rozwiązaniu zadania: nazwa FmtWrite nie pojawia się potem ani razu. Samo wciągnięcie traita do zasięgu jest całym zadaniem, bo bez tego makro write! na Stringu nie ma się do czego odwołać.
Klamry {C, D} biorą kilka nazw z jednej ścieżki, a {self, C} dokłada do nich sam moduł. Przy wyliczeniu (enum) zapis use election::Method::{self, Approval, Star}; pozwala pisać w matchu gołe Star — co czyta się świetnie, dopóki w pliku jest jedno wyliczenie, i fatalnie, gdy są trzy i każde ma swój Star. Some, None, Ok i Err siedzą w std::prelude właśnie dlatego, że akurat przy nich ta dwuznaczność nie powstaje.
Najgorsza pułapka to gwiazdka. Dwa globy — use election::*; i use other::*; — z których każdy niesie SEATS, kompilują się bez słowa skargi; błąd E0659 („SEATS is ambiguous”, „ambiguous because of multiple glob imports”) wyskakuje dopiero w miejscu użycia nazwy. Kod stoi więc spokojnie miesiącami i psuje się w dniu, w którym ktoś dopisał nazwę w cudzym module — w pliku, którego nawet nie dotykałeś, a nowa nazwa z góry potrafi po cichu przesłonić lokalną. Dwa miejsca, w których glob jest w porządku, łączy jedna właściwość: nazwy mogą pochodzić dokładnie z jednego źródła — use my_crate::prelude::* (moduł istniejący po to, żeby go tak wciągać) oraz use super::* na początku modułu #[cfg(test)], gdzie źródłem jest plik, który i tak właśnie czytasz.
Szukaj po polsku: deklaracja use w Ruscie · moduły i zasięg nazw · rust use is not an import · rust E0252 defined multiple times · rust E0659 ambiguous glob import