Rust “use” vs. C++ “using namespace”

2020-07-06 02:00发布

问题:

Is it considered bad style to declare multiple "use" statements in Rust? I am a C++ programmer that recently began trying out Rust. One thing I've noticed as I review Rust code is that in many Rust programs there will be a bunch of use statements at the top of the program. Coming from C++, it was discouraged to use using namespace std especially when making header files, but that doesn't seem to be the case in most of the Rust programs I've seen. So which of the following trivial examples is considered to be better Rust programming style? Does it change if you're making a binary program vs. a module? And why?

use std::sync::Arc;
use std::sync::Mutex;
use std::thread::Thread;
use std::rand::random;

fn main() {
    let mut vec: Vec<char> = (0u8..10).map(|i| i as char).collect();

    let mut data = Arc::new(Mutex::new(vec));
    for i in 1usize..10 {
        let data = data.clone();
        let thread = Thread::spawn(move || {
            let mut data = match data.lock() {
                Ok(guard)   => guard,
                Err(e)      => panic!("{}, was poisoned", e)
            };
            data.push(random::<char>());
        });
    }
}

Or this...

fn main() {
    let mut vec: Vec<char> = (0u8..10).map(|i| i as char).collect();

    let mut data = std::sync::Arc::new(
                            std::sync::Mutex::new(vec)
                                        );
    for i in 1usize..10 {
        let data = data.clone();
        let thread = std::thread::Thread::spawn(move || {
            let mut data = match data.lock() {
                Ok(guard)   => guard,
                Err(e)      => panic!("{}, was poisoned", e)
            };
            data.push(std::rand::random::<char>());
        });
    }
}

回答1:

You are probably confused by the similarly looked names (use and using namespace). In fact they are very different in semantics.

using namespace in C++ includes the whole contents of a namespace into the current scope, so, for example, you can use cout instead of std::cout:

using namespace std;

cout << "Hello!\n";

use in Rust, however, includes only the specified name, so you still have to qualify which item you are actually referring to:

use std::mem;
use std::fmt::Debug;

fn do_something<T: Debug>(t: T) { ... }

fn main() {
    let (mut x, mut y) = ...;
    mem::swap(&mut x, &mut y);
}

Note how Debug is used without qualifiers but swap still requires a module qualifier, so use in Rust is more like using (without namespace) in C++. Because use is very specific in what it imports, it is considered a good style to use it almost always, so your first example is the idiomatic one.

In fact, using namespace is more like glob imports in Rust:

use std::*;

And glob imports indeed are somewhat discouraged. However, Rust structuring conventions are more flexible that the ones used by C++ (in particular, in the std library), so use std::* won't give you the whole standard library, only the top-level modules. Glob imports are also useful in a couple of other situations, e.g. when reexporing names from another module or assembling all library extension traits in one module.

And no, conventions do not change regardless of if you're writing a library or an executable. Rust does not have anything like C/C++'s headers with literal inclusions, so each compilation unit is completely independent and can have any imports you like - it won't affect its users (unless these are pub use's, of course).



回答2:

using System; in C# is totally normal and in Java, you even don't need to write import java.lang.*; as it is already imported by default. And I don't see any flaw in writing multiple statements - if you need to call something with the same name, you can always call identifiers with their full name, as you did in your second code example.

P.S.: Sorry for writing an opinion driven answer^^