Use

Let’s see the main usages of the use keyword.

01. Bind a full path to a new name

Mainly use keyword is used to bind a full path of an element to a new name. So the user doesn’t want to repeat the full path each time.

// -- Initial code without the `use` keyword --
mod phrases { 
  pub mod greetings { 
    pub fn hello() { 
      println!("Hello, world!");
    }
  }
}

fn main() { 
  phrases::greetings::hello(); // Using full path
}


// -- Usage of the `use` keyword --
// 01. Create an alias for module
use phrases::greetings;
fn main() { 
  greetings::hello();
}

// 02. Create an alias for module elements
use phrases::greetings::hello;
fn main() { 
  hello();
}

// 03. Customize names with the `as` keyword
use phrases::greetings::hello as greet;
fn main() { 
  greet();
}

02. Import elements to scope

Another common usage of use is importing elements to scope. Remember that, this is also a bit similar to creating an alias and using it instead of using the full path.

fn hello() -> String {
  "Hello, world!".to_string()
}

#[cfg(test)]
mod tests {
  use super::hello; // Import the `hello()` function into the scope
    
  #[test]
  fn test_hello() {
    assert_eq!("Hello, world!", hello()); // If not using the above `use` statement, we can run same via `super::hello()`
  }
}

💡 By default, use declarations use absolute paths, starting from the crate root. But self and super declarations make that path relative to the current module.

Same way the use keyword is used to import the elements of other crates including the std, Rust’s Standard Library.

// -- 01. Importing elements --
use std::fs::File;

fn main() {
    File::create("empty.txt").expect("Can not create the file!");
}


// -- 02. Importing module and elements --
use std::fs::{self, File} // `use std::fs; use std::fs::File;`

fn main() {
    fs::create_dir("some_dir").expect("Can not create the directry!");
    File::create("some_dir/empty.txt").expect("Can not create the file!");
}


// -- 03. Importing multiple elements --
use std::fs::File;
use std::io::{BufReader, BufRead}; // `use std::io::BufReader; use std::io::BufRead;`

fn main() {
    let file = File::open("src/hello.txt").expect("file not found");
    let buf_reader = BufReader::new(file);

    for line in buf_reader.lines() {
        println!("{}", line.unwrap());
    }
}

We don’t need to use extern crate std; when using the std library. We will discuss more about this under the Standard Library section.

💡 use statements import only what we’ve specified into the scope, instead of importing all elements of a module or crate. So it improves the efficiency of the program.

03. Re-exporting

Another special case is pub use. When creating a module, you can export things from another module into your module. So after that, they can be accessed directly from your module. This is called re-exporting.

// ↳ main.rs
mod phrases;

fn main() {
    phrases::hello(); // Not directly map
}

// ↳ phrases/mod.rs
pub mod greetings;

pub use self::greetings::hello; // Re-export `greetings::hello` to phrases

// ↳ phrases/greetings.rs
pub fn hello() {
  println!("Hello, world!");
}

This pattern is quite common in large libraries. It helps to hide the complexity of the internal module structure of the library from users. Because users don’t need to know/follow the whole directory map of the elements of the library while working with them.