-
Notifications
You must be signed in to change notification settings - Fork 13.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Inconsistency in trait item resolution between Box<Trait>
and impl Trait
#41221
Comments
As documented in #54610, I noticed this while removing boxed returns from the let mut stream = ...;
stream.write_all(b"foo").unwrap(); Note that the return type was never named - and yet the change of |
Adding to the inconsistency, this only affects existential impl trait, not universal impl trait (or the mostly-equivalent trait bound): mod not_in_scope {
pub trait Foo {
fn foo(&self);
}
impl Foo for () {
fn foo(&self) {}
}
}
fn existential_impl_trait() {
fn f() -> impl not_in_scope::Foo {
()
}
f().foo();
}
fn universal_impl_trait(x: impl not_in_scope::Foo) {
x.foo();
}
fn dyn_trait(x: &dyn not_in_scope::Foo) {
x.foo();
}
fn trait_bound<T: not_in_scope::Foo>(x: T) {
x.foo();
}
pub fn main() {
existential_impl_trait();
dyn_trait(&());
universal_impl_trait(());
trait_bound(());
} Errors on |
I would argue that not having to import a trait into scope for I think extending it to existential |
I feel like that's a more-intuitive (and less-common) breakage to have though; Returning Additionally, unlike moving from I hope I communicated my thoughts clearly, please let me know if there's anything you'd like me to clear up. Hopefully I didn't come off as harsh ^^' |
Perhaps I should make my old argument more clear. If a public function function declares a function that returns However, if a public function returns For instance, imagine a crate that declares the following function. pub fn do_stuff() -> impl std::future::Future<Output = ()> {
todo!()
} It should be possible to change pub struct DoStuff(());
impl std::future::Future for DoStuff {
type Output = ();
fn poll(self: std::pin::Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> std::task::Poll<()> {
todo!()
}
}
pub fn do_stuff() -> DoStuff {
DoStuff(())
} However, if |
Yes, that does make more sense when applied to specifically future; that said, I'm still not quite seeing the reason why anyone would change Your future example definitely moves me closer to an undecided stance, but honestly I think treating trait methods as inherent would make sense (if I return |
Consider this example:
Here we are trying to call methods of traits that are not in scope.
Typically such methods are not resolved, but there's an exception - trait objects.
Trait objects are magic - when you call a method of
Trait
on a value of typeTrait
thenTrait
is automatically considered in scope (or maybe this method is treated as inherent, I haven't looked how exactly the implementation works). This is the reason whym::dynamic_tr().method()
works. Even if this is a special case, it's a reasonable special case - if you have a value of typeTrait
in hands, you typically want to call some trait methods on it and it would be a nuisance to requireTrait
imported in scope for this.I don't know what RFC or PR introduced these exact rules, but the fact is that they are used in practice.
All the logic above is applicable to
impl Trait
, but this special case doesn't apply to it andm::static_tr().method()
reports an "unresolved method" error.The text was updated successfully, but these errors were encountered: