Reputation: 814
In learning this new fascinating language I wrote this code which outputs 0 through 10 multiplied by 3:
pub struct Multiplier {
factor : int,
current : int
}
impl Multiplier {
pub fn new(factor : int) -> Multiplier {
Multiplier {
factor: factor,
current: 0
}
}
}
impl Iterator<int> for Multiplier {
fn next(&mut self) -> Option<int> {
if self.current > 10 {
None
}
else {
let current = self.current;
self.current += 1;
Some(current * self.factor)
}
}
}
struct Holder {
x : Multiplier
}
impl Holder {
pub fn new(factor : int) -> Holder {
Holder {
x : Multiplier::new(factor)
}
}
fn get_iterator(&self) -> Multiplier {
self.x
}
}
fn main() {
let mut three_multiplier = Holder::new(3).get_iterator();
for item in three_multiplier {
println!("{}", item);
}
}
If I change Holder
from this
struct Holder {
x : Multiplier
}
to this:
struct Holder {
x : Iterator<int>
}
I get a compilation warning:
<anon>:27:9: 27:22 error: explicit lifetime bound required
<anon>:27 x : Iterator<int>
^~~~~~~~~~~~~
Can anyone explain why changing the field type requires an explicit lifetime? I know how to mark the lifetimes but I'm not sure why the compiler wants me to do this.
Upvotes: 3
Views: 456
Reputation: 65832
You can add a lifetime bound this way:
struct Holder<'a> {
x: Iterator<int>+'a
}
Note however that this way, the Holder
struct is unsized, because it contains a trait object, which is unsized. This severely limits what you can do with the type: for example, you can not return a Holder
directly.
You can fix this by making Holder
accept a type parameter:
struct Holder<T> where T: Iterator<int> {
x : T
}
Let's change get_iterator
to use the type parameter:
impl<T> Holder<T> where T: Iterator<int>+Copy {
fn get_iterator(&self) -> T {
self.x
}
}
Note: I added the Copy
bound here because get_iterator
currently returns a copy. You could avoid the bound by making get_iterator
return a &T
instead.
As for new
, you'll have to completely redesign it. If we keep it as is, the compiler gives error if we call it as Holder::new
because Holder
now has a type parameter, and the compiler cannot infer a type because new
doesn't use any. To solve this, we can make new
generic by using a trait to provide the constructor:
trait FactorCtor {
fn new(factor: int) -> Self;
}
And then changing new
to use that trait:
impl<T> Holder<T> where T: Iterator<int>+FactorCtor {
pub fn new(factor : int) -> Holder<T> {
Holder {
x : FactorCtor::new(factor)
}
}
}
Because we have only one implementation of FactorCtor
in this program, the compiler manages to infer T
when calling Holder::new
. If we add another implementation, e.g. Adder
:
pub struct Adder {
factor : int,
current : int
}
impl FactorCtor for Adder {
fn new(factor: int) -> Adder {
Adder {
factor: factor,
current: 0
}
}
}
impl Iterator<int> for Adder {
fn next(&mut self) -> Option<int> {
if self.current > 10 {
None
}
else {
let current = self.current;
self.current += 1;
Some(current + self.factor)
}
}
}
Then we get a compiler error:
<anon>:72:9: 72:29 error: unable to infer enough type information about `_`; type annotations required
<anon>:72 let mut three_multiplier = Holder::new(3).get_iterator();
^~~~~~~~~~~~~~~~~~~~
We can fix this by specifying T
explicitly:
fn main() {
let mut three_multiplier = Holder::<Multiplier>::new(3).get_iterator();
for item in three_multiplier {
println!("{}", item);
}
}
Upvotes: 4