`impl` scoped imports

When you have a method that if cfg gated, when you invoke auto import using rust-analyzer, it will put the import at the top of the function. Even if you're importing something from the signature, which will not see the import from the body.

What if we allowed imports to be in the impl block itself? This change is as backwards compatible as it gets, syntax is intuitive and it feels natural:

struct Foo;

#[cfg(feature = "baz")]
impl Foo {
    use some_crate::Bar;

    pub fn baz(bar: Bar) {
        bar.baz();
    }
}
2 Likes

If I do pub use in that position, will it make Foo::Bar an alias for some_crate::Bar?

2 Likes

That point doesn't feel intuitive, agree. I don't think we want to introduce a new feature into the language, so probably not Foo::Bar. But maybe we can forbid pub use altogether?

There is currently one proposal for using that use syntax there for something different: inherent methods/traits. For instance, use Write (or use Write::write_all for a specific method). I'm trying to track down the state of that proposal.

This could be useful even when cfg is not involved, when a trait implementation depends on many items from the trait’s crate that aren’t otherwise used in the file. I've wished for it many times.

5 Likes

Maybe that can fill that pub use hole? :grinning_face_with_smiling_eyes:

1 Like

This would be amazing! It should even work inside functions too (I wanted a macro that did this kind of thing more than once; the alternative is to define a new f_Something item). And also inside struct definitions, etc.

fn f() {
   pub use somecrate::Something;
}

// now f::Something refers to somecrate::Something

Also seconding the notion that inherent methods should be pub use! Having just use affect the public API is unexpected.

1 Like

By the way, can you please share your use case? Like, I'm genuinely interested, but can't really come up with a situation when I ever wanted f::Something, to the point that f_Something was good enough. Some js libraries like interact.js come to mind.

2 Likes

This suggests that this should be supported:

fn foo() -> foo::Result {
    pub struct Result {
        bar: i32,
        baz: i32,
    }

    Result {
        bar: 1,
        baz: 2,
    }
}

Not sure what I think about it...

1 Like

I absolutely love this. Much better than defining a FooResult outside the function.

1 Like

That kind of just seems like it's trying to reuse a keyword for efficiency, I might prefer a syntax like

impl FooWriter {
    use std::io::Write;

    fn write_all = Write::write_all;
}

Or just have inheritance use a different keyword entirely.

1 Like

I think the need is more pressing for traits and structs. In the dynosaur crate, we must specify the name of each generated type

#[dynosaur::dynosaur(DynSomething)]
trait Something {
    ...
}
fn do_this(iter: &mut DynSomething<'_>) {
   ...
}

But if we had public inner items (not associated types - just items that could be free floating, but are scoped inside another item for organizational purposes), the trait object type could be spelled Something::Dyn rather than DynSomething.

Now for functions, I really would like to refer to the return type of a function, and a #[macro] could help with that (something like, define a type alias myfunction_Ret or, better, myfunction::Ret). RFC 2515 could help with that too, but it would require making the return type of the function impl Trait, which doesn't let you call inherent methods, so it's not always applicable.

Another thing: for each function, define a struct that carries all parameters of this function. Bon does that, but it generally either creates a name for this type or lets you choose a custom name; it would be cool if I could scope this name inside the function (like myfunc::Builder)

1 Like

Given that's just a tooling issue that seems like a weak motivation for this. I also went ahead and finally fixed that bug now fix: Fix import insertion not being fully cfg aware by Veykril · Pull Request #19890 · rust-lang/rust-analyzer · GitHub

1 Like

Maybe we can also reuse the syntax use .. as .. to do something great (just a quick thought)?

trait Foo {
    fn foo();
}

trait Bar {
    type BarFoo: Foo;

    fn bar() {
        // For now, we must specify qualified path, otherwise the compiler will say ambiguity
        <Self::BarFoo as Foo>::foo();
        // Maybe we can:
        use <Self::BarFoo as Foo>::foo as barfoo;
        barfoo();
    }
}

It's even more annoying when the associated type get nested. In some situations, I even have to write :sob:

<<<Self::Foo as Foo>::Bar as Bar>::Baz as Baz>::baz()

Yeah, I agree. But it anyway would be nicer to just have them inside, instead of playing with cfgs outside. And folks added other ideas for pub use

When doing type programming, you just repeat those signatures over and over. That might've simplified things.

this works:

pub fn foo() -> foo::Result { 
    foo::Result {}
}

mod foo {
    pub struct Result {}
}
1 Like