DEV Community

SomeB1oody
SomeB1oody

Posted on

[Rust Guide] 7.5. Keyword Use Pt. 2 - Re-exports

If you find this helpful, please like, bookmark, and follow. To keep learning along, follow this series.

7.5.1 Re-importing Names with pub use

After using use to bring a path into scope, that name is private within the lexical scope.

Using the code from the previous article as an example:

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

use crate::front_of_house::hosting::add_to_waitlist;  

pub fn eat_at_restaurant() {  
    add_to_waitlist();  
}
Enter fullscreen mode Exit fullscreen mode

For external code, eat_at_restaurant is accessible because it was declared with the pub keyword, but external code cannot see the add_to_waitlist used inside eat_at_restaurant, because items imported with use are private by default. If you want external code to access it as well, you need to add pub in front of use:

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

pub use crate::front_of_house::hosting::add_to_waitlist;  

pub fn eat_at_restaurant() {  
    add_to_waitlist();  
}
Enter fullscreen mode Exit fullscreen mode

This allows external code to access the item brought in with use.

When we want to expose code publicly, we can use this technique to adjust the outward-facing API instead of following the internal code structure exactly. In this way, the internal structure and the outward view of the code may differ a bit. After all, the person writing the code and the person calling the code usually expect different things.

To summarize: pub use both re-exports the item into the current scope and makes that item available for external code to import into their scope.

7.5.2 Using External Packages

First, add the package name and version of the dependency to Cargo.toml, and Cargo will download that package and its dependencies from crates.io to your local machine (you can also use an unofficial crate and fetch it from GitHub, but that is strongly discouraged). Then use use in the code to bring the specific item into scope.

Do you remember the guessing game from Chapter 2? Back then we needed the rand package to generate random numbers. We will still use rand as an example:

Step 1: Modify Cargo.toml

Open your project’s Cargo.toml file, and under [dependencies], write the package name and version, connected with =:

[package]  
name = "RustStudy"  
version = "0.1.0"  
edition = "2021"  

[dependencies]  
rand = "0.8.5"
Enter fullscreen mode Exit fullscreen mode

Step 2: Import the Package in Source Code

To use something from a package, just use use to import the corresponding path. Here I need the function that generates random numbers, so I import the parent module of that function, Rng, like this:

use rand::Rng;
Enter fullscreen mode Exit fullscreen mode

The Rust standard library, std, is also treated as an external package, but it is built into Rust itself, so you do not need to add it to Cargo.toml. You can just import it in the source code with use, which is somewhat like libraries such as re, os, and ctype in Python.

For example, if we want to import the HashMap struct from the collections module under std, we write:

use std::collections::HashMap;
Enter fullscreen mode Exit fullscreen mode

No changes to Cargo.toml are needed.

7.5.3 Cleaning Up Many use Statements with Nested Paths

Sometimes you use multiple items from the same package or module, and the beginning of the path is the same, but you still have to write it repeatedly. If there are many imports, writing them one by one is not practical. Rust therefore allows nested paths to simplify imports on a single line. This is similar to the brace expansion feature in bash.

The format is:

use common_part::{different_part1, different_part2, ...}
Enter fullscreen mode Exit fullscreen mode

Look at an example:

use std::cmp::Ordering;
use std::io;
Enter fullscreen mode Exit fullscreen mode

They share the common part std, so they can be rewritten with a nested path:

use std::{cmp::Ordering, io};
Enter fullscreen mode Exit fullscreen mode

If one import is a subpath of another import, Rust also allows the self keyword when using nested paths, as shown below:

use std::io;
use std::io::Write;
Enter fullscreen mode Exit fullscreen mode

This can be shortened to:

use std::io::{self, Write};
Enter fullscreen mode Exit fullscreen mode

7.5.4 The Wildcard *

Using * brings all public items in a path into scope. For example, if I want to import all public items from the collections module under the std library, I can write:

use std::collections::*;
Enter fullscreen mode Exit fullscreen mode

But this kind of import must be used very carefully, and is usually avoided.

Its use cases are:

  • Importing all tested code into the test module during testing
  • Sometimes used in prelude modules

Top comments (0)