This idea has been brought up several times. Iām not very excited about it for several reasons.
@sinkuuās example is a good example of how its confusing. Weād have to validate that every T you pass to Foo could implement Quack if an impl is in scope, without resolving it to a specific impl. Then, it would work, but you couldnāt call any methods on Foo without importing that impl.
Whatās especially concerning, then, is that these two blocks could do totally different things:
{
use my::QuackForDuck;
foo.action();
}
{
use another::QuackForDuck;
foo.action();
}
This is especially concerning because you might import your quack impl to make a duck variable quack, without realizing youāre changing how foo behaves in that scope.
Additionally, you now have to check the imports to figure out whatās going on in every scope. You canāt just assume because youāve seen the impl of this trait for this type before, you know what it does in this context. That seems quite bad.
We also havenāt considered how this interacts with the unnamed impls that non-orphan crates are still allowed to provide; this would have to shadow them, otherwise adding an impl to your crate is always a breaking change. But that just makes it even harder to track whatās going on.
This is all especially concerning with glob imports, which now might include an impl for types and traits that arenāt even from the same crate the globād module is from.
None of this is impossible - that is, we could define semantics that would still guarantee that in a given context, a single impl is found for every trait/type pair - but to me it seems much more confounding than the current behavior.
It just doesnāt seem better than the alternative - a newtype struct:
pub struct MyDuck(pub Duck);
impl Quack for MyDuck { }
fn foo() {
let my_duck = MyDuck(duck);
my_duck.quack();
let duck = my_duck.0;
}
And we could provide some ānewtype derivingā solution so that MyDuck would delegate a lot of impls to Duck without you writing it all out.