You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
use std::sync::Arc;traitFoo{}structBar;implFooforBar{}fntakes_foo(foo:&Foo){}fnmain(){let foo:Arc<Foo> = Arc::new(Bar);takes_foo(&foo);}
However, it seems the compiler is attempting to coerce &Arc<Foo> to a trait object before attempting deref coercion, which gives this unintuitive error:
rustc 1.15.1 (021bd294c 2017-02-08)
error[E0277]: the trait bound `std::sync::Arc<Foo>: Foo` is not satisfied
--> <anon>:13:15
|
13 | takes_foo(&foo);
| ^^^^ the trait `Foo` is not implemented for `std::sync::Arc<Foo>`
|
= note: required for the cast to the object type `Foo`
Adding a deref-reref seems to fix the issue, but it's definitely a papercut:
@nikomatsakis@nrc Wasn't sure who to ping here, just looking for a judgement on whether this behavior is intended or if it's something that can be improved.
@abonander hmm yes I agree this is suboptimal. I'm not sure what fix, if any, to do though. For one thing, if we made changes here, it might break code that is using the existing behavior -- and it seems like we'd be trying to make a very narrow targeted change here? (i.e., to detect the case where the trait object would be the same as the result of the deref).
Well the problem that I have isn't that trait object coercion supersedes deref coercion, it's that the compiler doesn't try deref coercion when trait object coercion fails. If we got that working, then it wouldn't break anything, it would just make more code compile that didn't before.
@abonander It's tricky, because it fails later, and that's required so it doesn't fail if more inference is required, although we could compromise a bit more here? Still, I'm not too happy about more hacks.
Reversing the order I think would introduce subtle breakage because if you have impl Foo for Box<Foo> then it will hit the inner impl first, which is unintuitive.
I was wondering if any further progress had been made on this? I'm encountering several examples of this defect in code i've been constructing that is using custom smart pointers, and the added derefs are clunky and unhelpful.
In addition to the above example, methods can normally be called by passing self as an argument, but not in this case where a trait object would require deref coercion:
trait Foo {
fn foo_method(&self);
}
struct Bar;
impl Foo for Bar {
fn foo_method(&self) {}
}
fn main() {
let foo: &Foo = &Bar{};
foo.foo_method(); // OK
Foo::foo_method(foo); // OK
let foo: Box<Foo> = Box::new(Bar);
foo.foo_method(); // OK
Foo::foo_method(&foo); // Error
}
This seems like an ergonomics consistency issue/bug. Playground link
Here's the same issue, this time passing a boxed Bar to a generic parameter which implements Foo:
trait Foo {}
struct Bar;
impl Foo for Bar {}
fn takes_foo_generic<T: Foo>(foo: &T) {}
fn main() {
let bar = Bar;
let boxed_bar = Box::new(Bar);
takes_foo_generic(&bar); // OK
takes_foo_generic(&boxed_bar); // error[E0277]: the trait bound `std::boxed::Box<Bar>: Foo` is not satisfied
}
I think these inconsistencies are confusing, especially for new users, and especially in the example of Box<T> which otherwise acts pretty much like T.
I guess this is the expected behaviour? Check RFC401
Note that we do not perform coercions when matching traits (except for receivers, see below). If there is an impl for some type U, and T coerces to U, that does not constitute an implementation for T. For example, the following will not type check, even though it is OK to coerce t to &T and there is an impl for &T:
structT;traitTrait{}fnfoo<X:Trait>(t:X){}impl<'a>Traitfor&'aT{}fnmain(){let t:&mutT = &mutT;foo(t);//~ ERROR failed to find an implementation of trait Trait for &mut T}
Is this the same issue? It seems related, but slightly different since in this case the trait is generic, rather than the function?
structMyType;implFrom<&[u8]>forMyType{fnfrom(_:&[u8]) -> Self{Self}}fntakes_slice(_:&[u8]){}fnmain(){takes_slice(b"");let _ = MyType::from(b"");//~ ERROR the trait bound `MyType: From<&[u8; 0]>` is not satisfied}
At least in this case, there is a nice diagnostic help: the trait `From<&[u8]>` is implemented for `MyType`. (playground).
Still, this feels a bit unexpected since From::from seems like it should behave the same as a free function, but actually requires explicit conversion to slice instead.
Function arguments are coercion sites but MyType::from is not a normal function. Rather it is an ambiguous reference to a whole set of functions (one for each From impl). Consequently explicitly choosing the right one from the set helps coercion to kick in: it works with let _ = <MyType as From<&[u8]>>::from(b""); but you probably want MyType::from(&b""[..])
The original test case (updated):
use std::sync::Arc;traitFoo{}structBar;implFooforBar{}fntakes_foo(foo:&dynFoo){}fnmain(){let foo:Arc<dynFoo> = Arc::new(Bar);takes_foo(&*foo);// remove * for errorlet foo:Box<dynFoo> = Box::new(Bar);takes_foo(&*foo);// remove * for error}
Box<dyn Foo> could have its own impl Foo which would lead methods of Foo called in takes_foo to be different from the ones resulting from coercing.
Given this analysis I'm inclined to agree with @ldm0 that this is at worst a diagnostic issue.
I'm not sure how difficult it would be to make these cases suggest a possible fix.
Activity
abonander commentedon Mar 6, 2017
@nikomatsakis @nrc Wasn't sure who to ping here, just looking for a judgement on whether this behavior is intended or if it's something that can be improved.
nikomatsakis commentedon Mar 6, 2017
@abonander hmm yes I agree this is suboptimal. I'm not sure what fix, if any, to do though. For one thing, if we made changes here, it might break code that is using the existing behavior -- and it seems like we'd be trying to make a very narrow targeted change here? (i.e., to detect the case where the trait object would be the same as the result of the deref).
cc @rust-lang/lang
eddyb commentedon Mar 6, 2017
@nikomatsakis We could try to just reverse the order, I believe both fall through if they fail.
abonander commentedon Mar 6, 2017
Well the problem that I have isn't that trait object coercion supersedes deref coercion, it's that the compiler doesn't try deref coercion when trait object coercion fails. If we got that working, then it wouldn't break anything, it would just make more code compile that didn't before.
eddyb commentedon Mar 6, 2017
@abonander It's tricky, because it fails later, and that's required so it doesn't fail if more inference is required, although we could compromise a bit more here? Still, I'm not too happy about more hacks.
abonander commentedon Mar 6, 2017
Reversing the order I think would introduce subtle breakage because if you have
impl Foo for Box<Foo>then it will hit the inner impl first, which is unintuitive.nikomatsakis commentedon Mar 8, 2017
Yeah I'm wary of trying to change the order because of breakage.
HarkonenBade commentedon Apr 20, 2018
I was wondering if any further progress had been made on this? I'm encountering several examples of this defect in code i've been constructing that is using custom smart pointers, and the added derefs are clunky and unhelpful.
abonander commentedon Apr 20, 2018
I'm still thinking the compiler should attempt both coercions before erroring.
wpwoodjr commentedon May 14, 2019
In addition to the above example, methods can normally be called by passing self as an argument, but not in this case where a trait object would require deref coercion:
This seems like an ergonomics consistency issue/bug.
Playground link
wpwoodjr commentedon May 16, 2019
Here's the same issue, this time passing a boxed Bar to a generic parameter which implements Foo:
Playground link
wpwoodjr commentedon May 16, 2019
I think these inconsistencies are confusing, especially for new users, and especially in the example of Box<T> which otherwise acts pretty much like T.
ldm0 commentedon May 2, 2020
I guess this is the expected behaviour? Check RFC401
It's more like a diagnostic issue to me.
ian-h-chamberlain commentedon Feb 15, 2023
Is this the same issue? It seems related, but slightly different since in this case the trait is generic, rather than the function?
At least in this case, there is a nice diagnostic
help: the trait `From<&[u8]>` is implemented for `MyType`. (playground).Still, this feels a bit unexpected since
From::fromseems like it should behave the same as a free function, but actually requires explicit conversion to slice instead.hkBst commentedon Nov 7, 2025
Function arguments are coercion sites but
MyType::fromis not a normal function. Rather it is an ambiguous reference to a whole set of functions (one for each From impl). Consequently explicitly choosing the right one from the set helps coercion to kick in: it works withlet _ = <MyType as From<&[u8]>>::from(b"");but you probably wantMyType::from(&b""[..])The original test case (updated):
Box<dyn Foo>could have its ownimpl Foowhich would lead methods of Foo called in takes_foo to be different from the ones resulting from coercing.Given this analysis I'm inclined to agree with @ldm0 that this is at worst a diagnostic issue.
I'm not sure how difficult it would be to make these cases suggest a possible fix.