Bringing a trait alias into scope doesn't allow calling methods from its component traits
Nemo157 opened this issue · 11 comments
#![feature(trait_alias)]
mod some_module {
pub trait Foo {
fn foo(&self);
}
pub struct Qux;
impl Foo for Qux {
fn foo(&self) {}
}
pub trait Bar = Foo;
}
use some_module::{Bar, Qux};
fn use_baz(object: Qux) {
// Should this work because Bar is in scope?
object.foo();
}
(playground) currently produces an error:
error[E0599]: no method named `foo` found for type `some_module::Qux` in the current scope
--> src/lib.rs:21:12
|
8 | pub struct Qux;
| --------------- method `foo` not found for this
...
21 | object.foo();
| ^^^
|
= help: items from traits can only be used if the trait is in scope
help: the following trait is implemented but not in scope, perhaps add a `use` for it:
|
17 | use some_module::Foo;
|
cc #41517
@alexreg The thing I'm currently thinking about is what makes sense when you extend it beyond this simple example; in particular, if we say: trait Foo = TraitA + TraitB;
and then we use path::to::Foo;
, does that then bring into scope both TraitA
and TraitB
s methods? what if there is a conflict in naming?
In other words, consider:
#![feature(trait_alias)]
mod alpha {
pub trait A { fn foo(); }
pub trait B { fn foo(); }
pub trait C = A + B;
}
use alpha::C;
(this incidentally causes an ICE)
@nikomatsakis Thoughts on how feasible it would be to make this work, given the current state of the import system (about which I know little)?
If I were to import an alias, I'd likely often do so to call the methods off the trait as well as to set bounds. It'd be surprising if it didn't. If there is a name conflict, we can differentiate the way we do already: using UFCS with the specific trait, instead of the alias.
Is this the last thing needed to decide on for stabilization? The tracking issue makes it look that :D
@seanmonstar That's the last "feature" that really needs to be implemented, yeah. I'd be glad to help fix it, but I'd need some advice from someone who understand the import system, really. I've not touched that before.
I was playing around with this, trying to discover where the error happens, when I found that this doesn't happen if you make the function generic on the trait.
fn generic<T: Bar>(object: T) {
// works!
object.foo();
}
fn concrete_to_generic(object: Qux) {
// works!
generic(object);
}
As for if the trait alias contains multiple traits with the same name, the method conflict is already as expected: "multiple applicable items in scope"
, and qualifying with some_module::Fooz::foo(&object)
works.
I'm not sure what you mean. Could you post a complete example on the playground?
Seems like nightly on the playground is timing out for me, but works locally: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=92e4a4878c40c2a7a6af7a04f9cfd9ed
Ah right, I see. That makes sense because the type system resolves the type alias then, and not name resolution.
Nightly on the playground has been broken for a while now...
@seanmonstar
Candidates from bounds on a type parameters are treated like inherent and are found even if the trait is not in scope (the relevant code is in fn assemble_inherent_candidates_from_param
).
@Centril
The example seems equivalent to
#![feature(trait_alias)]
mod alpha {
pub trait A { fn foo() {} }
pub trait B { fn foo() {} }
// pub trait C = A + B;
impl A for u8 {}
impl B for u8 {}
}
// use alpha::C;
use alpha::*;
fn main() {
u8::foo(); // ERROR multiple applicable items in scope
}
, the ambiguity error is already reported during resolution of a specific method in this case.
One interesting effect is that the traits become in scope in the module that defines (rather than imports) the alias.
#![feature(trait_alias)]
mod m {
pub trait A { fn foo() {} }
pub trait B { fn bar() {} }
impl A for u8 {}
impl B for u8 {}
}
trait C = m::A + m::B;
fn main() {
u8::foo(); // OK
}