(Note: the following post uses edition 2018 syntax and rules to refer to items)
To understand what's going on, one needs to know there are two "places" Rust will look at when doing name resolution:
-
the module specific namespace;
-
a "global" context, called the prelude.
- (where
1.has priority over2.)
An example of 1. are the use ... imports, or items defined within the same module:
use ::log::info; // brings info! into the current module namespace
For 2., there are three main examples:
-
The standard prelude, this is the place that makes
Box,Vec,String,Option,Some, etc. be available. -
The "
externprelude". This is what makes external crates be callable by path, with an optional (but which I cannot recommend enough) leading::to disambiguate.
In the previous example, this is what made::logbe understood as the namespace of theextern crate log.-
For 99.9% of the crates out there, this "extern prelude" is automagically populated with all the external crates specified in the
Cargo.toml.In all but the few latest releases, for instance,
proc_macrowasn't part of that prelude when using aproc-macro = truecrate, hence the need (at "that" time) forextern crate proc_macro.- (Note that not writing it right now is an easy way to make your crate be less compatible with these "old" Rust versions for absolutely no reason, so I recommend to keep using
extern crate proc_macroin such crates for a few more Rust releases. That being said,1.45.0has been a major breakpoint in the world of procedural macros, so you can directly require that Minimum Rust Version and not care about these questions).
This is what leads to also requiring
extern crate testorextern crate alloc. - (Note that not writing it right now is an easy way to make your crate be less compatible with these "old" Rust versions for absolutely no reason, so I recommend to keep using
-
Now, regarding macros, the only way to use macros from another crate used to be through a #[macro_use] extern crate ..., which brought the macros to a global prelude-like context: the macros were not namespaced!
This can be quite ergonomic for things like ::log's macros, but in practice ends up being quite restrictive.
So, the more modern way to pull these items is through standard use imports, but then these need to be repeated within each module that needs to refer to them, which is no different than what we have to do for non-macro items anyways!
Hence the pattern for crates to offer their own public prelude, which is one of the few things that are idiomatic to blob import: this way, copy-pasting that import line isn't that annoying.
A personal pattern I have been using for some of my crates is to have my own internal prelude:
//! src/utils/prelude.rs
#![allow(unused_imports)]
pub(in crate)
use ::core::{
convert::{TryInto, TryFrom},
iter::FromIterator,
ops::Not,
};
#[cfg(feature = "alloc")]
pub(in crate)
use ::alloc::{
boxed::Box,
string::String,
vec::Vec,
};
pub(in crate)
use ::anyhow::{
bail,
Result; // If second param is elided, it defaults to anyhow's
};
pub(in crate)
use ::log::{
error, warn, info, debug, trace,
};
pub(in crate)
use crate::{
// stuff from my own crate
prelude::{*,
// If my crate _exports_ a prelude, it makes sense for it to be a subset of the internal one
},
};
and then have a use_prelude!(); macro at the root of my crate:
//! src/lib.rs
macro_rules! use_prelude {() => (
#[allow(unused_imports)]
use crate::utils::prelude::*;
)}
so that I can simply copy-paste that use_prelude!(); import in all my modules, end emulate my own custom prelude.