What's the difference between 'extern crate' and write into cargo.toml

what's the difference between 'extern crate' and write into cargo.toml

Dependencies must be written into Cargo.toml to be used.

extern crate is almost never needed anymore. It used to be required to use the dependencies in code prior to the 2018 edition of rust. Now, if you include edition = “2018” in the [package] section of Cargo.toml, you can just use foo::bar; to use a dependency without needing to specify extern crate foo; in main or lib.

extern crate is used to explicitly add the dependency to namespace. It can reference the toolchain-provided dependencies, such as proc_macro or test, which are neither pulled in from other sources nor added implicitly.
Cargo.toml's dependencies are the additional dependencies, provided not by the toolchain, but by the external sources, like crates.io, git repository or local directory.

The proc_macro crate is actually added implicitly as of a couple releases ago.

thanks I got it !

Do you mean these two ways for different dependencies source? But I say an example "extern crate rand" so I am a little confuse?

In 2015 edition (i.e. if there's no "edition" field in Cargo.toml), every external crate should be explicitly used with extern crate directive. In 2018 edition, everything put in Cargo.toml is implicitly available, and extern crate, although it can be used as before, is reserved for the sysroot crates, which aren't declared in Cargo.toml. So, if you're using the 2018, extern crate rand is unnecessary and can be replaced by use rand (or removed entirely, if you already have use rand::something).

1 Like

In Rust 2015 you needed both, an entry in the cargo.toml and extern crate.
The difference between the two is subtle:

  • The entry in the cargo.toml declares a dependency on a package. A package can contain multiple crates. However, the vast majority of packages only contain a single crate.
  • extern crate declares which crates are used from the above packages.

This was considered too complicated (most packages only contain a single crate anyway) and thus it was changed for Rust 2018: Now all crates from dependent packages can be used without an explicit extern crate declaration.

That is confusing. Colloquially one would expect a "crate" to hold many "packages" not the other way around. But never mind.

It is still confusing. I was confounded the other day when taking the log crate into use.

I have 'edition = "2018"' and 'log = "0.4"' in my Cargo.toml

I have :

extern crate log; 
...
info!("Some informative message.");

In my code.

I was wondering why I cannot use :

use log;
...
info!("Some informative message.");

like I do for everything else?

If I use "use log;" then I also have to write 'log::info!("Some informative message.");'

Which is a bit clunky so I'm left with the mysterious 'extern crate log;' sticking out like a wart in my code.

I suspect it’s because you’re importing all macros by way of #[macro_use] extern crate log;

With edition 2018, use log; doesn’t have that same effect — instead, either bring each item you want to use into scope with e.g.,

use log::{info, debug};

Or perhaps use log::*;.

Oooor, specify the full path when you’re making use of the macro, like log::info!(...).

Make sense?

(Docs for this change: https://doc.rust-lang.org/edition-guide/rust-2018/macros/macro-changes.html)

Yes I agree. To be honest it never even occurred to me because English is not my native language.
It's even more confusing because crates.io talks about "crates" everywhere while it's called package in the cargo documentation (e.g. [package] in cargo.toml).

Cargo serves a double role as package manager and build script:

  • The dependencies in cargo.toml come from the package manager side of cargo, it is about distribution. It refers to some unit ("package" or "crate") that is fetched from crates.io and may contain other artifacts that are not strictly relevant for compilation.
  • The extern crate declaration is about compilation. "crate" means only the specific library in that context.

However the line between the two is quite blurry.

No. I just had extern crate log; in my main.rs.

Your suggestions do indeed make sense.

However the end result is not quite the same.

Originally I had extern crate log; in my main.rs and only there. In my other files I had nothing, no use log::* or such. I just wrote info!(....); wherever I wanted.

Now I have to put the "use log::*;" in every file or write things out long hand log::info!(...) .

Something is a bit different.

There is a possibility of confusion if you have extern crate foo and get all the macros in that crate. Say that crate foo has a macro info!(). You could end up using the foo version of info!() because you didn't also have extern crate log. Of course, the compiler would detect the conflict if you had both, but I don't know how you'd work around the name conflict if you needed macros from both crates. The current behavior you describe fixes that problem.

Huh, I'm not sure what might be going on. Trying this:

extern crate log;

fn main() {
    info!("hi there!");
}

(playground link)

results in a compilation failure (regardless of edition setting). adding #[macro_use] to the extern crate log line compiles. As does the following with edition 2018:

use log::info;

fn main() {
    info!("hi there!");
}

(playground link)

Ah, sorry, you are right. I went back and looked at by old commits. What I actually had in my main was:

#[macro_use]
extern crate log;

In my VSCode the '#[macro_use]' line is colored so faintly that I hardly notice it is there!

Anyway if I replace those two lines in main.rs with:

use log::*;

then all my other modules fail to compile until I add it to those as well, or write log::info... etc everywhere.

So something different I don't understand is going on between using 'extern crate log' and 'use log::*'

??

For this purpose, #[macro_use] extern crate log; in main.rs or lib.rs would be equivalent to putting use log::* in every file in the crate.

The macro use feature I think was a consequence of how macros were coded in the compiler but the fact that you could only bring them in the global scope while useful for something like log was not exactly desirable since I’m not sure how it even handled conflicting macros.

1 Like

Yeah, that is what I found.

Hmm...

So can I conclude that using use log::* in all my files is the recommended way to do it?

The documentation for the log crate still uses the 'extern'.

Personally, I just write log::info and so on everywhere, it's a matter of preference I think. Docs still having unnecessary extern crates is a pet peeve of mine and I hope they're gradually removed to avoid more confusion in the future.

OK, that clear that up. Thanks.

log::info works for me. Although generally I like to keep as much syntactic punctuation noise out of my source as possible.

To much <'>:: line noise everywhere make my eyes lose focus on what I'm looking at.

(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:

  1. the module specific namespace;

  2. a "global" context, called the prelude.

  • (where 1. has priority over 2.)

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 "extern prelude". 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 ::log be understood as the namespace of the extern 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_macro wasn't part of that prelude when using a proc-macro = true crate, hence the need (at "that" time) for extern 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_macro in such crates for a few more Rust releases. That being said, 1.45.0 has been a major breakpoint in the world of procedural macros, so you can directly require that Minimum Rust Version and don't care about these questions).

      This is what leads to also requiring extern crate test or extern crate alloc.


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.

6 Likes