3
votes

I'm trying to make a trait to present something as iterator over &Strings. If I use struct std::slice::Iter in get_iterator result everyting is ok.

pub trait Value {
    fn get_iterator(&self) -> Box<std::slice::Iter<String>>;
}

struct StringList {
    strings: Vec<String>
}

impl Value for StringList {
    fn get_iterator(&self) -> Box<std::slice::Iter<String>> {
        Box::new(self.strings.iter())
    }
}

fn main() {
}

But when I switch to trait Iterator<&String>

pub trait Value {
    fn get_iterator(&self) -> Box<Iterator<Item=&String>>;
}

struct StringList {
    strings: Vec<String>
}

impl Value for StringList {
    fn get_iterator(&self) -> Box<Iterator<Item=&String>> {
        Box::new(self.strings.iter())
    }
}

fn main() {
}

rust complains about lifetimes:

<anon>:11:31: 11:37 error: cannot infer an appropriate lifetime for lifetime parameter 'a in function call due to conflicting requirements
<anon>:11         Box::new(self.strings.iter())

How should I define trait and make implementation to make it work?

By the way, why does compiler call this lifetime 'a? It is not named anywhere and usually rust complies about "anonymous lifetime". Is it a bug that should be reported?

1
I don't think you can box anything that has a non-static lifetime. Also you shouldn't use '&String' but '&str' when taking a reference. Also I don't see where the trait you mentioned in the title is.oli_obk
I'm talking about result of get_iterator method. In first case it is struct (std::slice::Iter<&String>) and in the second case it is trait (Iterator<Item=&String>). &String here is just an example, in project I want to use more complex things. If I use &str here, I'll have to create another struct for Iterator<Item=&str> because std::slice::Iter<&String> cannot be auto-casted to std::slice::Iter<&str>.Slava Baginov
And actually I can box something having non-static lifetime: fn get_first_item(x: &Vec<String>) -> Box<&str> { Box::new(&x[0]) } works just fine.Slava Baginov

1 Answers

2
votes

We want to declare a new lifetime on our StringList reference and make sure our returned, boxed Iterator trait is bound by that lifetime.

pub trait Value {
    fn get_iterator<'a>(&'a self) -> Box<Iterator<Item=&String> + 'a>;
}

struct StringList {
    strings: Vec<String>
}

impl Value for StringList {
    fn get_iterator<'a>(&'a self) -> Box<Iterator<Item=&String> + 'a> {
        Box::new(self.strings.iter())
    }
}

fn main() {
}

Edit: To answer your question about the lifetime 'a, it actually is defined, but the docs don't show it. Click on the Iter return object in the Vec documentation and you will see that 'a is declared on that object. The implementation for Vec would have to introduce the lifetime 'a to the Vec reference taken when calling iter().