Reputation: 58875
I have an Iterator that produces Fibonacci numbers. I restricted the type to u32
, but now I'm struggling to make it generic for any numeric type.
Working, non-generic code:
struct Fib {
value: u32,
next: u32,
}
impl Fib {
fn new( a : u32, b : u32 ) -> Fib {
Fib { value : a, next : b }
}
}
impl Iterator for Fib {
type Item = u32;
fn next(&mut self) -> Option<u32> {
let value = self.value;
let next = self.value + self.next;
self.value = self.next;
self.next = next;
Some( value )
}
}
//////////////////////////////////////////////////
fn main() {
let fib = Fib::new( 1, 2 );
let sum = fib.filter( |x| { x % 2 == 0 })
.take_while( |&x| { x <= 4000000 })
.fold( 0, |sum, x| { sum + x });
println!("{}", sum);
}
The issue is that the implementation of Iterator
requires a constraint to Num
, but I don't know how to express this:
impl <T : Num> Iterator for Fib<T> { ... }
Produces:
use of undeclared trait name `Num`
And when I try either use std::num::{Num}
or use num::traits::{Num}
, I am told that the modules do not exist.
Upvotes: 4
Views: 3529
Reputation: 1008
I don't think you want Fib
to be generic over numeric types, but types that implement the +
operator. Like so:
use std::ops::Add;
struct Fib<N>
where N: Add<Output = N> + Copy {
value: N,
next: N,
}
impl<N> Iterator for Fib<N>
where N: Add<Output = N> + Copy {
type Item = N;
fn next(&mut self) -> Option<N> {
let next = self.value + self.next;
self.value = self.next;
self.next = next;
Some(next)
}
}
fn main() {
let fib_seq = Fib {
value: -1,
next: 1,
};
for thing in fib_seq.take(10) {
println!("{}", thing);
}
}
Add
is the trait that allows you to use the +
operator and produce Output
. In this case N
implements the Add<Output = N>
trait which means N + N
will produce something of type N
.
That sounds like it, but when you try to do self.next + self.value
you are moving value
and next
out of self
which causes an error.
You can't get away with not moving the values since the definition of add has this method signature:
fn add(self, rhs: RHS) -> Self::Output;
RHS
in Add
's case is just Self
. So in order to restrict N
it to types that can just be copied with little overhead I added the Copy
trait as a restriction.
OP mentions an interesting point: Is it possible to alias traits? In short no. You could make a new trait:
trait SimpleAdd: Add<Output = Self> + Copy {
}
But then you would have to implement that trait for all the types you wanted. I.e. i32
does not automatically implement SimpleAdd
. But you can do it with generics if you wanted:
impl<N> SimpleAdd for N
where N: Add<Output = N> + Copy {
}
So the above two blocks will get you the same thing as a trait alias, but it seems like a hassle.
Upvotes: 4