Question

Rust `use` vs `extern crate`: What’s the Difference?

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 crate was historically used to make an external crate available to your crate.
  • use brings names from a module or crate path into the current scope.

In other words:

  • extern crate connected your program to another crate.
  • use made 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 the rand crate available.
  • use rand::Rng; brought the Rng trait 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 crate is like registering that the building is part of your campus.
  • use is 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 available
  • use = 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.

Take Quiz

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?

  • HashMap lives at std::collections::HashMap
  • use lets us refer to it simply as HashMap
  • No extern crate is needed for std

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.

Take Quiz

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

Featureuseextern crate
Main purposeBring names into scopeMake an external crate available in older Rust
Used in modern RustYes, constantlyRarely
Works with local modulesYesNo
Works with external cratesYesHistorically yes
Shortens long pathsYesNo
Usually needed todayYesUsually not

Full path vs use

StyleExampleWhen to use
Full pathstd::collections::HashMap::new()Good for one-off usage
use importuse std::collections::HashMap;Better when used repeatedly

Older Rust vs modern Rust

Rust styleExample
Olderextern crate rand; use rand::Rng;
Modernuse rand::Rng;

use vs dependency declaration

ConceptWhere it goesPurpose
Dependency declarationCargo.tomlTells Cargo which crates your project depends on
useRust source fileBrings names into scope

A common beginner confusion is treating use as if it replaces Cargo.toml. It does not.

Take Quiz

Cheat Sheet

Quick reference

Modern Rust

  • Add external crates in Cargo.toml
  • Use use to 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 use for repeated or clearer names
  • Learn extern crate mainly 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.

Take Quiz

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 rand crate to Cargo.toml
  • Import HashMap from the standard library with use
  • Import the Rng trait so random number methods work
  • Store at least three key-value pairs in a HashMap
  • Generate and print one random number
Take Quiz

Keep learning

Related questions

View more