Alright, so this is a new idea and not at all fleshed out and I would love for criticism. I am not a compiler dev so this discussion is about design, not implementation.
The solution I am proposing would treat trait implementations in a similar way to how we already interact with generics. We now define a types implementation signature
with one of these possible syntax options (haven't decided what's best):
MyType<T,U>|OtherTrait from other_crate|
or
MyType<T,U, OtherTrait from other_crate>
or
MyType<T,U, path::to::named::impl>
Omitting the implementation (ie. using the regular type signature) would mean that the behaviour would be identical to the current operation. If the trait is required and is not implemented for the type under the current orphan rules then we get a comp error.
Under the hood, the compiler is treating each of these items as distinct types (similar to what we're manually doing today). Function signatures that use these types don't need to change. They don't care about the implementation details since different implementations are actually just distinct types.
We can have explicit conversions between implementations like this:
let x = MyType::new(); // implementation is unspecified so we use defaults
let y = x as MyType|OtherTrait from better_crate|;
I know there have been other new type proposals but this has one main advantage. With this system there is no fragmentation of types. MyType
is still a MyType
. Functions can directly accept MyType
and the compiler can keep track of it's implementation signature
. To clarify, fn(MyType)
would work out of the box with both MyType|OtherTrait from other_crate|
and MyType|OtherTrait from better_crate|
. All functions are automatically generic over a types trait implementations.
This could then interact with type aliases to make things look a little nicer.
type MyModifiedType = MyType|OtherTrait from other_crate|;
I think something like this could massively improve code reusability. I'd love to hear about what you guys think.