Skip to content

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

use election::*;   // has SEATS
use other::*;      // also has SEATS

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: import executes a module, and use does not — there is no import side effect, no import order, no circular-import failure, and no cost. from x import y is use x::y, import x as z is use x as z, and from x import * is the glob with the same reputation. Python's __init__.py re-exporting names is pub use, which this page's neighbour covers. One genuine trap for Python programmers: a use inside 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. use is closest to the ALIASES statement inside a class — a shorter local name for something whose real name is long — and the as rename is exactly ALIASES ... 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#. import and using are the same "shorten a name" mechanism, and Java's single-type-import versus on-demand import x.* is exactly the named-versus-glob split, with the same advice. C#'s using X = Y; alias is Rust's as.
  • C++. using declarations and using 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

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