Fully qualified syntax for trait's methods

I write Rust code just as examples or tutorials for students.
I add an incredible amount of comments, but I like the code to be self-explanatory as much as possible.
Importing namespaces with use is great for making the code concise, but it can confuse the students. Where does all this functions, methods, structs come from?
I like to use "fully qualified syntax" for my examples.
I am confused with the need to "import traits into scope" for fairly simple things. In case of need, I import it as close to the code that needs it.

    pub fn parse_output(output: std::process::Output) -> Vec<String> {
        // import trait into scope for lines()
        use std::io::BufRead;
        for x in output.stdout.lines() {
            // do something
        }
    }

I would like to use the "fully qualified syntax", but using rust-analyzer to change it, it becomes different and not readable.


    pub fn parse_output(output: std::process::Output) -> Vec<String> {
        for x in std::io::BufRead::lines(output.stdout) {
            // do something
        }
    }

Is there another syntax I can use?
Something like:

for x in output.stdout::<std::io::BufRead>.lines() {

I cannot find anything like that.

Thanks

The idiomatic thing to do is to import the trait, typically at the top of the file. If your students are going to actually learn Rust, they'll have to learn about it at some point. So I recommend at least a hybrid approach so they don't get the idea that using fully qualified paths is the norm.

I guess R-A can't figure out how the method actually resolved. Here's a couple rewrites with some additional comments:

use std::io::BufRead;

pub fn parse_output(output: std::process::Output) -> Vec<String> {
    // You lose method resolution autoref/slice-unsizing, so you need the
    // explicit `&*` in this case    vv
    for x in std::io::BufRead::lines(&*output.stdout) {}
    
    // Sometimes you have to be more explicit, but on the upside being
    // explicit in one place can make more coercions implicit elsewhere.
    // You only need `&` in this case (but you still don't get autoref).
    for x in <&[_] as std::io::BufRead>::lines(&output.stdout) {}

    // This is the idomatic way though, in combination with the trait import
    // at the top of the file.
    for x in output.stdout.lines() {}
    
    vec![]
}

More reading:

If you want method call syntax, you need to import the trait (i.e. just use the idiomatic form).

4 Likes

If I am not mistaking, when writing library macros the idiomatic thing to do is to neither rely on anything being in scope nor not being in scope. Which means one should always use ::core::path::to::Trait::method() (only for core and std), or $crate::path::to::Trait::method(), <T as ...::path::to::Trait>::method(), or use inherent methods which have precedence*: you cannot use trait methods without bringing traits in scope inside macros and you also cannot rely on absense of traits in scope which have methods with the same name.

* One can also either write generated code inside a new module or rely on generic parameters not implementing traits you have not specified they implement if you are generating functions.