This is a topic about Trait “deduction”: for all Type: TraitA
, we want Rust to know that we also have Type: TraitB
Currently, there are 2 ways of achieving this:
-
Generic implementations:
//! crate `generic_impl` pub trait TraitA { /* ... */ } pub trait TraitB { /* ... */ } // Generic implementation providing `T: TraitA => T: TraitB` impl<T: TraitA> TraitB for T { // implementation of `TraitB`'s associated functions / types, // only with access to / knowledge of `TraitA`'s }
extern crate generic_impl; use ::generic_impl::*; struct SomeType { /* ... */ } impl TraitA for SomeType { // implementation of `TraitA`'s associated functions / types, // without access to / knowledge of `TraitB`'s // but with access to `SomeType` } fn assert_implements_TraitB<T: TraitB> () {} static assert_SomeType_implements_TraitB: fn() = assert_implements_TraitB::<SomeType>;
Examples: (
use ::std::*;
)-
T: fmt::Display
implies thatT: string::ToString
-
U: convert::From<T>
implies thatT: convert::Into<U>
-
-
//! crate `supertrait` pub trait TraitB {} // Supertrait (access to `TraitB`'s functions/types while impl-ing `TraitA`) pub trait TraitA : TraitB { /* ... */ }
extern crate supertrait; use ::supertrait::**; struct SomeType { /* ... */ } impl TraitA for SomeType { // implementation of `TraitA`'s associated functions / types, // with access to / knowledge of `TraitB`'s // and with access to `SomeType` } // Error: `TraitB` is not implemented for `SomeType`
Examples:
Copy: Clone
,Fn: FnMut
,FnMut: FnOnce
…
Now, even though both give the same “deduction rules”, they do it differently:
- in the former case, you get
Type: TraitB
for free and, unless the generic implementation is just a default (overridable) one (cf. specialisation RFC), it cannot be overriden; - in the latter case, you have to provide the
Type: TraitB
implementation yourself;
Now, take the case of Copy : Clone
(or Fn : FnMut : FnOnce
), what is the reason behind having Copy
be a supertrait of Clone
(i.e. Copy: Clone
) instead of having a generic (default) implementation of Clone
for all T: Copy
?
- When implementing the
Copy
marker trait for a type, it’s weird that we have to explicitely give the obvious implementation|&self| *self
; - It is even weirder (in this particular case), that we are allowed to give a different implementation!
Obviously, (2) is subjective, and with specialisation and default impls, we can still allow it (and thus avoid breakage of Clone
impls) while getting rid of (1) if we replaced the extension Trait relation with a generic impl.
And the exact same thing could be said of Fn : FnMut : FnOnce
shenanigans.
Finally: for those saying that it’s no big deal since we have #[derive(Clone, Copy)]
, you should know that:
-
derive
's heuristics are not very smart (One example) - There is (currently) no
#[derive(FnMut, FnOnce)]
for a struct for which we implementFn