Why do so many examples use 'extern crate'?

mod my_module; declares a module named my_module within the current namespace, use my_module::my_func; then imports my_func from the module my_module into the current namespace.

I personally think of use as using a binding into the namespace (despite that being bad english) so am not strongly attached to the import meaning. But mod definitely does not "import" anything, it is only a declaration saying that "there exists a module here, defined in another file".

Yeah, I was mentioning how convenient a "global blob import" can be for crates such as ::log, but I am not advocating to use that pattern everywhere, for the reasons you mentioned.
Any kind of logic-critical macros should be using the edition 2018 way of referring to things, so that within one single file one can know what is in scope.


  • (Emphasis mine)

Indeed, "import" is a word that can carry a lot of meaning for people coming from other programming environments.

  • The fact that the meaning of the word import is non-trivial is indeed justified. In languages such as Python, once a package has been installed on the system / virtual environment, the only requirement for a script to use it is for the source code to include an import statement of some form (or a dynamic call for crazier people), which is indeed different from Rust (with Cargo, and from 2018 onwards), whereby a(n external) package is automagically present within a crate by having it listed under the Cargo.toml [dependencies], at which point the Rust source code can already refer to its items by absolute path.

So let's try to just avoid supposing what the true meaning of import is, and instead, let's just clarify what happens within Rust:

  • In Rust, the way I would phrase it to avoid being ambiguous, it that use is used to bring into scope, that is, to have stuff within arm's reach rather than having to walk a bit to get it (that would be using a fully-qualified path): you can write Rust programs using external crates without any use "statement" whatsoever!

    • For instance, I personally try to avoid "unnecessary" use "statements", since they add a burden on the reader (that of having to check at the beginning of the file the list of use "statements"), or even on the writer (without IDE support, especially when adding and removing stuff, since it can lead to unused_imports afterwards), where "unnecessary" are the use "statements" that bring an item into scope just for it to be used once (or even twice).
      Example:

      • Instead of:

        use ::std::process::Command;
        
        // later (either many lines later, or just two lines later if within the same function body)
        
        Command::new(...)
            .args(...)
            .status()
            ...
        
      • (I) do:

        ::std::process::Command::new(...)
            .args(...)
            .status()
            ...
        
    • Another thing is to avoid use-ing items in a way that strips them of their prefix:

      • Instead of:

        use ::std::{
            iter::once,
            ptr::NonNull,
            rc::{Rc, Weak},
        };
        
        // Many lines later:
        
        once(...) // once what?
        
        thing: NonNull<u32>, // NonZeroU32?
        
        parent: Weak<...> // what kind of weak?
        
      • I do:

        use ::std::{iter, ptr, rc::{self, Rc}};
        
        // Many lines later:
        
        iter::once(...); // much clearer
        
        thing: ptr::NonNull<u32>, // ditto
        parent: rc::Weak<...>,
5 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.