Reputation: 133
I'm a Rust newbie and I'm trying to figure out what the best way to do the following in Rust is:
struct ThingIterator {
current: String,
stop: String,
}
impl Iterator for ThingIterator {
type Item = &str;
fn next(&mut self) -> Option<&str> {
if self.current == self.stop {
return None;
}
// For testing
self.current = self.stop;
Some(&self.current)
}
}
fn main() {
let pi = ThingIterator {
current: String::from("Ask"),
stop: String::from("Zoo"),
};
println!("Number of things={}", pi.count());
}
My error is:
error[E0106]: missing lifetime specifier
--> src/main.rs:7:17
|
7 | type Item = &str;
| ^ expected lifetime parameter
error: aborting due to previous error
This makes sense, I need to specify how long the reference returned from next() is going to be valid. I'm guessing for the function itself it's fine as the lifetime is elissed (not sure about the conjugation of elission) - but I somehow need to define the lifetime of the "type Item = &str" row.
In my case it will be valid as long as "current" is valid, i.e. the same lifetime as "self".
I haven't seen anything in the Rust book or other documentation that helps me figure out this case.
P.S. Sorry if I'm butchering the nomenclature, I'm very much new to Rust. Thanks
Upvotes: 4
Views: 779
Reputation: 10424
The lifetime on &mut self
in next
isn't in scope when defining the type Item
, so Item
can't depend on that lifetime. Instead, it's typical to have ThingIterator
hold references instead of owned data. If there's still a structure that has owned data, you'll probably implement IntoIterator
for &OwnsData
to convert into the type that uses references.
// ThingIterator is now generic in the lifetime 'a
// and it holds references rather than owned Strings.
struct ThingIterator<'a> {
current: &'a str,
stop: &'a str,
}
impl<'a> Iterator for ThingIterator<'a> {
// Now we can use the lifetime from ThingIterator here.
type Item = &'a str;
fn next(&mut self) -> Option<&'a str> {
if self.current == self.stop {
return None;
}
// For testing
self.current = self.stop;
Some(self.current)
}
}
// Typically, you'll have a type that owns its data
// Like Vec<T>, HashSet<T>, etc.
struct OwnsData {
current: String,
stop: String,
}
impl OwnsData {
// We'll have the traditional method that takes a reference
// to self and returns an iterator over references into self.
// Explicit lifetimes aren't needed, but it might help with understanding.
// fn iter<'a>(&'a self) -> ThingIterator<'a> {
fn iter(&self) -> ThingIterator {
ThingIterator {
current: &self.current,
stop: &self.stop,
}
}
}
// Then, we'll implement IntoIterator for references to OwnsData
// using the OwnsData::iter method defined above.
// This is helpful because for loops and many iterator methods
// use IntoIterator to work.
impl<'a> IntoIterator for &'a OwnsData {
// We'll be converting into ThingIterator
type IntoIter = ThingIterator<'a>;
type Item = &'a str;
fn into_iter(self) -> ThingIterator<'a> {
self.iter()
}
}
fn main() {
let pi = ThingIterator {
current: "Ask",
stop: "Zoo",
};
println!("Number of things={}", pi.count());
// Alternatively, we could start with Strings
// and use OwnsData
let tau = OwnsData {
current: "Ask".to_string(),
stop: "Zoo".to_string(),
};
println!("Number of things={}", tau.iter().count());
}
See also
P.S. the word you're looking for is "elided".
Upvotes: 6
Reputation: 13942
You can't, yet. This would require generic associated types (GATs). As of today, it is still just a RFC.
The current Iterator
/ Stream
API is sometimes called "detached":
The idea is that
Item
that gets returned byStream
is “detached” fromself
, which means that it can be stored and moved about independently fromself
.
Hypothetically, after GAT is landed, one would be able to write something like:
trait AttachedStream {
type Item<'s> where Self: 's;
// note the `'s` here!
fn poll_next<'s>(
self: Pin<&'s mut Self>,
cx: &mut Context<'_>,
) -> Poll<Option<Self::Item<'s>>>;
// ^^^^
// `'s` is the lifetime of the `self` reference.
// Thus, the `Item` that gets returned may
// borrow from `self`.
}
That's exactly what you want. Check out Niko's async interview #2 for more interesting details.
Upvotes: 3