Bringing Paths into Scope with the use Keyword

The use keyword creates shortcuts to paths, reducing repetition. Think of it as creating local aliases, similar to import statements in JavaScript/TypeScript.

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

The use statement creates a shortcut valid only within its scope:

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

use crate::front_of_house::hosting;

mod customer {
    pub fn eat_at_restaurant() {
        hosting::add_to_waitlist();
    }
}

Idiomatic use Patterns

Functions: Import the parent module, not the function directly

// Idiomatic
use crate::front_of_house::hosting;
hosting::add_to_waitlist();

// Less clear where function is defined
use crate::front_of_house::hosting::add_to_waitlist;
add_to_waitlist();

Structs/Enums/Types: Import the full path

use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert(1, 2);
}

Name conflicts: Use parent modules to disambiguate

use std::fmt;
use std::io;

fn function1() -> fmt::Result {
    // --snip--
    Ok(())
}

fn function2() -> io::Result<()> {
    // --snip--
    Ok(())
}

Providing New Names with the as Keyword

Create aliases to resolve naming conflicts:

use std::fmt::Result;
use std::io::Result as IoResult;

fn function1() -> Result {
    // --snip--
    Ok(())
}

fn function2() -> IoResult<()> {
    // --snip--
    Ok(())
}

Re-exporting Names with pub use

Make imported items available to external code (similar to TypeScript’s export { ... } from ...):

mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

pub use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
}

Re-exporting allows exposing a different public API structure than your internal organization.

Using External Packages

Add dependencies to Cargo.toml:

rand = "0.8.5"

Then import and use:

use std::io;

use rand::Rng;

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    println!("The secret number is: {secret_number}");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    println!("You guessed: {guess}");
}

Standard library (std) is available automatically but still requires explicit use statements.

Nested Paths

Combine multiple imports from the same crate/module:

use rand::Rng;
// --snip--
use std::{cmp::Ordering, io};
// --snip--

fn main() {
    println!("Guess the number!");

    let secret_number = rand::thread_rng().gen_range(1..=100);

    println!("The secret number is: {secret_number}");

    println!("Please input your guess.");

    let mut guess = String::new();

    io::stdin()
        .read_line(&mut guess)
        .expect("Failed to read line");

    let guess: u32 = guess.trim().parse().expect("Please type a number!");

    println!("You guessed: {guess}");

    match guess.cmp(&secret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => println!("You win!"),
    }
}

Use self to import both the parent and child:

use std::io::{self, Write};

The Glob Operator

Import all public items (use sparingly):

use std::collections::*;

Commonly used in test modules and when implementing prelude patterns. Can make code less clear and cause naming conflicts.