I feel like Rust completely lacks inheritance, for the sake of avoiding code duplication or boilerplate. I think the best way to add the good parts of inheritance to Rust is to add the ability for subtraits to:
- Override default implementations of their supertraits
- Implement required methods of their supertraits
And then in the rust docs for each subtrait you'll see the total required methods to implement on the list on the left to make it clear if the subtrait has already implemented some of the supertraits.
So then code like this will be possible:
trait FromCookies {
fn from_cookies(&str) -> Self;
}
trait TokenAccount: FromCookies + serdeDeserialize {
fn ::from_cookies(&str) -> Self {
// use deserialize and stuff...
}
}
#[derive(Deserialize)]
struct Account { ... }
impl TokenAccount for Account;
This allows easy code duplication, inheriting functionality from TokenAccount by letting it implement other traits!
Example: ExactSizeIterator's new implementation with proposed functionality
I can also take the ExactSizeIterator subtrait as an example, it doesn't have any required methods, but the documentation instructs you to override the size_hint implementation of its supertrait, Iterator. So optimized implementations can use the len provided trait method from ExactSizeIterator, that gets its info from size_hint, hopefully improving performance.
I feel like implementing ExactSizeIterator is unclear, and forces you to read the docs. I know this may sound stupid but I feel like having documentation is a privilege, and for the same reason I think documentation should not be required in order to understand how to use something. A language as expressive as Rust should be (and usually is) understandable without documentation in my opinion.
Which is why I think with the proposed subtrait can implement supertrait functionality, ExactSizeIterator should require a len method, which is what the developer implements when implementing ExactSizeIterator, instead of implementing size_hint from Iterator. This len method is instead of the current len provided method from ExactSizeIterator that just returns a usize that it gets from the implemented size_hint. But before the "old" (current) len returns the usize, it makes sure with assert_eq! that both of the bounds received from size_hint are equal.
I can see 2 performance gains from this new implementation, ("old" len being the current implementation in std):
- Old
len returns a usize but it gets it from an fn size_hint -> (usize, Option<usize), so memory is wasted from the unneeded bounds. New len just returns a usize because it is the literal implementation from the developer.
- Old
len makes sure both the bounds returned from the size_hint are equal, as a guarantee, with assert_eq!. For the same reason as the first performance gain, new len doesn't need to check anything.
current len source code from std (the one referred to as "old" len)
Maybe the compiler already optimizes away the "faults" I noted with the old len when compiling with optimizations. But I still think it could speed up optimized compilations because there are fewer things to optimize (maybe that's how it works?) and that it will also optimize non-optimized debug builds, of course.
And lastly, because of the proposed functionality, ExactSizeIterator can override the default size_hint from Iterator in order to keep the old functionality like so:
trait ExactSizeIterator: Iterator {
fn ::size_hint(&self) -> (usize, Option) {
let len = self.len();
(len, Some(len))
}
}
This avoids the boilerplate that there usually is when implementing ExactSizeIterator, where the implementor needs to return (len, Some(len)) from size_hint instead of just len. Though this is very little boilerplate, I imagine it could be much more significant for more complex subtraits.
Conclusion
I feel like traits are a huge zero cost abstraction, they are the core of polymorphism and modularity in Rust. But with their current implementation they are limited in the reusability aspect. I think a "subtrait can implement supertrait" functionality would be effective at increasing reusability of code by essentially inheriting implementations, the good parts of inheritance (no expenses at runtime, right?).
I am probably getting ahead of myself and people who are far smarter than me who contribute to the language aren't adding this functionality for a reason but I'd like to know why that is and the opinion of anyone who sees this.