Item 23: Avoid wildcard imports

Rust's use statement pulls in a named item from another crate or module, and makes that name available for use in the local module's code without qualification. A wildcard import (or glob import) of the form use somecrate::module::* says that every public symbol from that module should be added to the local namespace.

As described in Item 21, an external crate may add new items to its API as part of a minor version upgrade; this is considered a backwards compatible change.

The combination of these two observations raises the worry that a non-breaking change to a dependency might break your code: what happens if the dependency adds a new symbol that clashes with a name you're already using?

At the simplest level, this turns out not to be a problem: the names in a wildcard import are treated as being lower priority, so any matching names that are in your code take precedence:

use bytes::*;

// Local `Bytes` type does not clash with `bytes::Bytes`.
struct Bytes(Vec<u8>);

Unfortunately, there are still cases where clashes can occur; for example, if the dependency adds a new trait and implements it for some type T, then if any method names from the new trait clash with existing method names in your own code for T

trait BytesLeft {
    // Method name clashes with wildcard-imported `bytes::Buf::remaining`...
    fn remaining(&self) -> usize;
}

impl BytesLeft for &[u8] {
    // ... and implementation for `&[u8]` clashes with `bytes::Buf` impl.
    fn remaining(&self) -> usize {
        self.len()
    }
}
        let arr = [1u8, 2u8, 3u8];
        let v = &arr[1..];
        assert_eq!(v.remaining(), 3);

then a compile-time error is the result:

error[E0034]: multiple applicable items in scope
  --> wildcard/src/main.rs:31:22
   |
31 |         assert_eq!(v.remaining(), 3);
   |                      ^^^^^^^^^ multiple `remaining` found
   |
note: candidate #1 is defined in an impl of the trait `BytesLeft` for the type `&[u8]`
  --> wildcard/src/main.rs:16:5
   |
16 |     fn remaining(&self) -> usize {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   = note: candidate #2 is defined in an impl of the trait `bytes::Buf` for the type `&[u8]`
help: disambiguate the associated function for candidate #1
   |
31 |         assert_eq!(BytesLeft::remaining(&v), 3);
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^
help: disambiguate the associated function for candidate #2
   |
31 |         assert_eq!(bytes::Buf::remaining(&v), 3);
   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^

As a result, you should avoid wildcard imports from crates that you don't control. There are a couple of common exceptions to this advice, though.

Firstly, if you control the source of the wildcard import, then the concerns given above disappear. For example, it's common for a test module to do import super::*;. It's also possible for crates that use modules primarily as a way of dividing up code to have:

mod thing;
pub use thing::*;

Secondly, some crates have a convention that common items for the crate are re-exported from a prelude module, which is explicitly intended to be wildcard imported:

use thing::prelude::*;

Although in theory the same concerns apply in this case, in practice such a prelude module is likely to be carefully curated, and higher convenience may outweigh a small risk of future problems.

Finally, if you don't follow the advice of this Item, consider pinning dependencies that you wildcard import to a precise version, so that minor version upgrades of the dependency aren't automatically allowed.