Lifetime for an Iterator field
While learning this exciting new language, I wrote this code that outputs 0 to 10 times 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 changed Holder
to this
struct Holder {
x : Multiplier
}
:
struct Holder {
x : Iterator<int>
}
I am getting a compile warning:
<anon>:27:9: 27:22 error: explicit lifetime bound required
<anon>:27 x : Iterator<int>
^~~~~~~~~~~~~
Can anyone explain why it takes an explicit lifetime to change the field type? I know how to mark the lifespan, but I'm not sure why the compiler wants me to do this.
source to share
You can add a binding to life like this:
struct Holder<'a> {
x: Iterator<int>+'a
}
Note that the structure is Holder
not supported in this way because it contains a tag object that is non-standard. This severely limits what you can do with a type: for example, you cannot directly return Holder
.
You can fix this by making an Holder
accept type parameter:
struct Holder<T> where T: Iterator<int> {
x : T
}
Let's change get_iterator
to use a type parameter:
impl<T> Holder<T> where T: Iterator<int>+Copy {
fn get_iterator(&self) -> T {
self.x
}
}
Note. I added here Copy
because it get_iterator
is currently returning a copy. You can avoid binding by doing get_iterator
instead &T
.
As for new
, you will have to completely redo it. If we keep it as is, the compiler will throw an error if we name it Holder::new
because it Holder
now has a type parameter, and the compiler cannot infer the type because it new
doesn't use. To solve this problem, we can make it new
generic with a trait to create a constructor:
trait FactorCtor {
fn new(factor: int) -> Self;
}
And then changing new
to using this trait:
impl<T> Holder<T> where T: Iterator<int>+FactorCtor {
pub fn new(factor : int) -> Holder<T> {
Holder {
x : FactorCtor::new(factor)
}
}
}
Since there is only one implementation in this program FactorCtor
, the compiler manages to deduce T
when called Holder::new
. If we add another implementation, eg. 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 explicitly specifying T
:
fn main() {
let mut three_multiplier = Holder::<Multiplier>::new(3).get_iterator();
for item in three_multiplier {
println!("{}", item);
}
}
source to share