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.

+3


source to share


1 answer


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);
    }
}

      

+3


source







All Articles