Question
In Rust, what is the difference between use and extern crate?
My current understanding is that use imports identifiers into the current scope, while extern crate declares an external crate. However, that distinction does not fully make sense to me.
Why does Rust have these two concepts, and when should each one be used?
Short Answer
By the end of this page, you will understand what use does, what extern crate historically did, why Rust had both, and how modern Rust code usually works without extern crate. You will also see practical examples, common mistakes, and how imports are typically written in real Rust projects.
Concept
use and extern crate are related, but they solve different problems.
The core idea
extern cratewas historically used to make an external crate available to your crate.usebrings names from a module or crate path into the current scope.
In other words:
extern crateconnected your program to another crate.usemade specific items easier to refer to.
Why this existed
Rust has a module system with paths, similar to file paths or namespaces in other languages. A path like std::io::Result tells Rust exactly where an item lives.
Originally, Rust required an explicit step to say:
"This external crate exists and I want to link it into my crate."
That step was extern crate.
Then, once the crate was available, use could bring names from it into scope:
extern crate rand;
use rand::Rng;
Here:
extern crate rand;made therandcrate available.use rand::Rng;brought theRngtrait name into scope.
Modern Rust
In Rust 2018 and later, extern crate is usually unnecessary. If a crate is listed in Cargo.toml, you can normally refer to it directly:
use rand::Rng;
So today:
- you usually use
use - you rarely write
extern crate
Why this matters
Understanding this helps you read older Rust code and understand the design of Rust's module system. You will still see extern crate in older tutorials, legacy projects, and some special cases.
Mental Model
Think of a crate like a building, and names inside it like rooms.
extern crateis like registering that the building is part of your campus.useis like putting a shortcut sign on your desk so you can quickly refer to a room inside that building.
Example:
- Without a shortcut, you might write the full path every time.
- With
use, you can write a shorter name.
So:
extern crate= make the outside library availableuse= bring a name into your local working area
In modern Rust, Cargo and the 2018 edition handle much of the "register the building" step for you automatically, so you mostly just write the shortcut with use.
Syntax and Examples
Basic syntax
Using use
use std::collections::HashMap;
This brings HashMap into the current scope, so you can write:
let map = HashMap::new();
instead of:
let map = std::collections::HashMap::new();
Using extern crate in older Rust
extern crate rand;
use rand::Rng;
This was the old style for external crates.
Example: use only in modern Rust
use std::collections::HashMap;
fn main() {
let mut scores = HashMap::new();
scores.insert("Alice", 10);
scores.insert("Bob", 20);
println!("{:?}", scores);
}
What happened here?
HashMaplives atstd::collections::HashMapuselets us refer to it simply asHashMap- No
extern crateis needed forstd
Example: importing from an external crate in modern Rust
If rand is in Cargo.toml:
[dependencies]
rand = "0.8"
then you can write:
use rand::Rng;
fn main() {
let mut rng = rand::thread_rng();
let n = rng.gen_range(1..=6);
println!("{}", n);
}
Why use rand::Rng is needed
gen_range is provided by the Rng trait. Bringing the trait into scope allows its methods to be used on the RNG value.
Aliasing with use
use std::collections::HashMap as Map;
fn main() {
let mut data = Map::new();
data.insert("x", 1);
}
This renames HashMap to Map within the current scope.
Step by Step Execution
Consider this example:
use std::collections::HashMap;
fn main() {
let mut counts = HashMap::new();
counts.insert("apple", 2);
counts.insert("banana", 3);
println!("{:?}", counts);
}
Step by step
1. use std::collections::HashMap;
Rust sees that you want the name HashMap available in this scope.
After this line, inside the current module, HashMap can be used as a short name for std::collections::HashMap.
2. let mut counts = HashMap::new();
Rust resolves HashMap using the use statement.
This is effectively the same as writing:
let mut counts = std::collections::HashMap::new();
3. counts.insert("apple", 2);
A key-value pair is inserted into the map.
4. counts.insert("banana", 3);
Another key-value pair is inserted.
5. println!("{:?}", counts);
The map is printed.
Important observation
use does not copy code, create a new type, or load a crate by itself. It only makes a path easier to access in the current scope.
In older Rust, if HashMap had come from an external crate instead of std, you might first have needed extern crate some_crate; before using paths from it.
Real World Use Cases
Where use is used constantly
Importing types
use std::fs::File;
use std::io::Read;
Used when working with files and input/output.
Importing traits
use rand::Rng;
Needed so trait methods become available.
Importing enums or variants
use std::cmp::Ordering;
Useful for matching comparison results.
Shortening long paths
use crate::services::user_service::UserService;
Common in larger applications with nested modules.
Where extern crate appears today
Mostly in:
- older Rust codebases
- old blog posts and tutorials
- legacy crates written before Rust 2018
- a few niche compatibility situations
If you are starting a new Rust project, you will usually not need it.
Real Codebase Usage
In real Rust projects, use is part of everyday code organization.
Common patterns
1. Grouping imports at the top of a file
use std::fs::File;
use std::io::{self, Read};
use std::path::Path;
This makes dependencies of the module easy to scan.
2. Importing your own modules with crate::
use crate::config::Settings;
use crate::errors::AppError;
This is very common in application code.
3. Bringing traits into scope for methods
use std::io::Read;
Without the trait import, method calls provided by the trait may not resolve.
4. Aliasing to avoid naming conflicts
use std::fmt::Result as FmtResult;
use std::io::Result as IoResult;
Useful when two modules export items with the same name.
5. Nested import syntax
use std::{fs::File, io::{self, Read}};
Helps keep imports compact.
About extern crate in real projects
Developers generally avoid it in modern code unless there is a specific compatibility reason. If you see it, it often signals that the code was written for an older Rust edition or has not been updated.
Common Mistakes
1. Thinking use loads or links a crate
use does not mean "download this library" or "link this crate." It only introduces a name into scope.
Broken assumption:
use some_crate::Thing;
This only works if some_crate is actually available as a dependency.
2. Thinking extern crate imports everything automatically
Older Rust code like this:
extern crate rand;
does not put all of rand into local scope. You still need full paths or use:
extern crate rand;
use rand::Rng;
3. Forgetting to import a trait
This is very common.
Broken code:
fn main() {
let mut rng = rand::thread_rng();
let n = rng.gen_range(1..=10);
println!("{}", n);
}
This may fail because the Rng trait is not in scope.
Fixed version:
use rand::Rng;
fn main() {
let mut rng = rand::thread_rng();
let n = rng.gen_range(1..=10);
println!("{}", n);
}
4. Confusing scope levels
A use inside one module does not automatically apply everywhere else.
mod a {
use std::collections::HashMap;
}
mod b {
fn test() {
// HashMap is not in scope here
}
}
Each module manages its own scope.
5. Using old extern crate style in new code without need
This is not usually wrong, but it is unnecessary in modern Rust and can confuse beginners.
Older style:
extern crate rand;
Modern style:
use rand::Rng;
plus a dependency entry in Cargo.toml.
Comparisons
use vs extern crate
| Feature | use | extern crate |
|---|---|---|
| Main purpose | Bring names into scope | Make an external crate available in older Rust |
| Used in modern Rust | Yes, constantly | Rarely |
| Works with local modules | Yes | No |
| Works with external crates | Yes | Historically yes |
| Shortens long paths | Yes | No |
| Usually needed today | Yes | Usually not |
Full path vs use
| Style | Example | When to use |
|---|---|---|
| Full path | std::collections::HashMap::new() | Good for one-off usage |
use import | use std::collections::HashMap; | Better when used repeatedly |
Older Rust vs modern Rust
| Rust style | Example |
|---|---|
| Older | extern crate rand; use rand::Rng; |
| Modern | use rand::Rng; |
use vs dependency declaration
| Concept | Where it goes | Purpose |
|---|---|---|
| Dependency declaration | Cargo.toml | Tells Cargo which crates your project depends on |
use | Rust source file | Brings names into scope |
A common beginner confusion is treating use as if it replaces Cargo.toml. It does not.
Cheat Sheet
Quick reference
Modern Rust
- Add external crates in
Cargo.toml - Use
useto bring names into scope - Usually do not write
extern crate
use
use std::collections::HashMap;
use std::io::{self, Read};
use crate::config::Settings;
use rand::Rng;
Old style extern crate
extern crate rand;
use rand::Rng;
What use can import
- types
- functions
- modules
- traits
- enum names
- enum variants with extra syntax
Important rule
use affects the current scope only.
Trait reminder
If a method comes from a trait, you often need the trait in scope:
use rand::Rng;
Dependency reminder
This is required for external crates:
[dependencies]
rand = "0.8"
Best practice
- Prefer modern import style
- Use full paths for one-off references
- Use
usefor repeated or clearer names - Learn
extern cratemainly to read older code
FAQ
Why does modern Rust usually not need extern crate?
Rust 2018 improved the module system so crates in Cargo.toml are generally available directly by name.
Is extern crate deprecated?
It is mostly unnecessary in modern Rust, but you may still see it in older code and some special cases.
Does use install a crate?
No. Dependencies are declared in Cargo.toml. use only brings names into scope in source code.
Can I use use with my own modules?
Yes. For example:
use crate::models::User;
Why do I sometimes need use some_trait::TraitName?
Because methods provided by traits are often only usable when the trait is in scope.
Should I remove extern crate from old code?
If the project uses Rust 2018 or later, you can often simplify imports by removing it, but test carefully after changes.
What is the difference between use and a full path?
A full path writes the complete location every time. use creates a shorter local name for convenience and readability.
Mini Project
Description
Build a small Rust program that uses both standard library imports and an external crate import. This project demonstrates the modern Rust import style and helps you see how use works in practice without relying on extern crate.
Goal
Create a command-line program that generates a random number and stores a few values in a HashMap using use imports.
Requirements
- Add the
randcrate toCargo.toml - Import
HashMapfrom the standard library withuse - Import the
Rngtrait so random number methods work - Store at least three key-value pairs in a
HashMap - Generate and print one random number
Keep learning
Related questions
Absolute Values in Rust: Using the abs() Method
Learn how to calculate absolute values in modern Rust with abs(), handle signed integer overflow, and choose safer alternatives.
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Access Enum Variant Values in Rust with Pattern Matching
Learn how to extract and print values stored in Rust enum variants using match and if let pattern matching, with practical examples.