I'm trying to understand how to implement a generic trait in Rust.
While I've seen a number of examples, the examples are too tied to a specific use (e.g. genomic mutators) for me to be able to understand at this point in my Rust development.
Instead, here's a simple example based on something fairly universal--incrementing:
trait Incrementable {
fn post_inc(&mut self) -> Self;
fn post_inc_by(&mut self, n: usize) -> Self;
}
impl Incrementable for usize {
fn post_inc(&mut self) -> Self {
let tmp = *self;
*self += 1;
tmp
}
//"Overload" for full generalizability
fn post_inc_by(&mut self, n: usize) -> Self {
let tmp = *self;
*self += n;
tmp
}
}
fn main() {
let mut result = 0;
assert!(result.post_inc() == 0);
assert!(result == 1);
assert!(result.post_inc_by(3) == 1);
assert!(result == 4);
}
The above code works, but is lacking because it isn't generalizable to all numeric types without writing a lot of boilerplate code.
In my attempts to generalize the above code, I've gotten into fights with the type system, borrow checker or been forced down a path of implementing FromPrimitive
for every type I want to support in my generic version (effectively putting me back to square one).
Can you help me understand how to implement Incrementable
generically,
such that post_inc()
and post_inc_by()
work for at least all integer and float types, ideally without having to write an implementation for each type?
I am hoping the answer will help me see how traits, implementations, types and associated types can work together in a more straightforward use case than I've been able to come across.
I'm on Rust 1.16.0.
@Simon Whitehead's example can easily be adapted for stable Rust:
While you need to do the implementation for each type, each of them is extremely simple.
You can also use a macro to hide repetitive implementations:
The types that we can increment need to
+=
(AddAssign
)Point 1. and 3. we can assure by using a trait bound, for point 2. we can set up a trait that has the function
one() -> self
.So here is a working example:
You don't have to write an implementation of
Incrementable
for each type, but you do have to implement the trait that supplies theone()
function. You can't get away without that, because for non numerical types it is not obvious what "increment by one" means.I kept everything in a generic implementation that can be implemented generically. The exception is the
T::one()
, so no boiler-plate code needed except this one trivial function for each type.You could do this with macros, following what the std did:
It is possible without macros if you use the
num
crate: