12

Where is the recommended place to put use declarations? I couldn't find any decisive answer in the book, in FAQs, mailing lists or online forums. I'm beginning a new project in Rust and I'd prefer to get the right approach right away.

Is one of the two below approaches recommended? Is it only for "aliasing" stuff or does it do more, like initialize a module if it hasn't been used before?

use std::io;
use std::io::Write;

fn some_func() -> () {
    [...] // We assume we need std::io here
}

fn some_other_func() -> () {
    [...] // We assume we need std::io and std::io::Write here
}

OR

fn some_func() -> () {
    use std::io;
    [...] // We assume we need std::io here
}

fn some_other_func() -> () {
    use std::io;
    use std::io::Write;
    [...] // We assume we need std::io and std::io::Write here
}
achedeuzot
  • 4,164
  • 4
  • 41
  • 56
  • 4
    There is a `fmt-rfcs` repo for discussing style related questions. In [this issue](https://github.com/rust-lang-nursery/fmt-rfcs/issues/24) they are discussing `use` imports -- without a clear consent so far. They also don't talk a lot about module-level imports vs function-level imports. In my experience, nearly all things are imported at module level, especially those used in multiple functions. Personally, I think I only use function-level imports when I have a very specialized import or a glob-import which I want to scope. But I guess a rule should be established in the linked issue. – Lukas Kalbertodt Aug 10 '17 at 16:38
  • Thanks for pointing at the issue discussion, it is very interesting ! – achedeuzot Aug 10 '17 at 16:50

1 Answers1

12

TL;DR: Like almost every other piece of software, it depends on what you are doing. The common style that I have observed (and prefer myself) is to put them at the top of the file and only moving them to narrower scope as needed.


Generally, I recommend starting by placing use statements directly after any extern crate and mod statements, separated by a blank line:

extern crate foo;
extern crate bar;

mod interesting;

use std::collections::HashMap;
use foo::Foo;
use bar::{Quux, moo};    
use interesting::something;    

// structs, functions, etc.

I base this default on the fact that — most times — an import is used in multiple top-level items. Thus, it makes sense to only import it once.

There are times where imported traits have conflicting methods, and in those cases I scope the import to where it's needed. There are also cases where I'm heavily dealing with a single enum and wish to glob-import it to avoid re-stating the enum's name:

fn foo(e: MyEnum) {
    use MyEnum::*;

    match e {
        One => 1,
        Two => 2,
    }
}

In certain cases, conflicting use statements indicate that you are attempting too much in a single file and it should be split into separate files and then the use statements are no longer ambiguous.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 3
    +1 for the enum case; I use it a lot to save typing in `match` (and line length), but it would really pollute the top-level module to use them there. – Matthieu M. Aug 10 '17 at 16:58