I have a struct ( DataSource
) that holds some data ( &[u8]
), and a custom iterator that iterates over it.
struct DataSource<'a> {
data: &'a Vec<u8>,
}
struct MyIterator<'a> {
source: DataSource<'a>,
}
impl<'a> Iterator for MyIterator<'a> {
type Item = &'a u8;
fn next(&mut self) -> Option<Self::Item> {
let ret = &self.source.data[0];
Some(ret)
}
}
Notice a few important things here:
Item
of the iterator has a lifetime. This is only possible because the lifetime is already used by one of the struct's fields - source
Item
s lifetime is 'a
, the lifetime of ret
must also be 'a
.Now, due to my use-case, I would like to add the following features:
DataSource
should also be able to own data
. data
is not Clone
. My initial solution was to replalce data: &'a [u8]
with data: D
where D: Borrow<[u8]>
:
struct DataHolder<D: Borrow<[u8]>> {
data: D,
}
struct MyIterator<D: Borrow<[u8]>> {
holder: DataHolder<D>,
}
impl<D: Borrow<[u8]>> Iterator for MyIterator<D> {
type Item = &u8;
fn next(&mut self) -> Option<Self::Item> {
Some(&self.holder.data.borrow()[0])
}
}
I thought this would work since due to blanket implementations both &[u8]
and [u8]
implement Borrow<[u8]>
. However this doesn't compile. The iterator's item is &u8
so it requires an explicit lifetime. MyIterator
doesn't have any lifetimes to reference so writing type Item = &'a u8
would result in an undeclared lifetime.
My next solution was to add phantom data and reference the lifetime through that:
struct DataHolder<'a, D: Borrow<[u8]>, T: 'a> {
data: D,
p: PhantomData<&'a T>,
}
struct MyIterator<'a, D: Borrow<[u8]>, T: 'a> {
holder: DataHolder<'a, D, T>,
}
impl<'a, D: Borrow<[u8]>, T> Iterator for MyIterator<'a, D, T> {
type Item = &'a u8;
fn next(&mut self) -> Option<Self::Item> {
Some(&self.holder.data.borrow()[0])
}
}
Which produces the following error:
error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
--> src\bin\main7c.rs:16:26
|
16 | Some(&self.holder.data.borrow()[0])
| ^^^^^^
|
note: first, the lifetime cannot outlive the anonymous lifetime defined here...
--> src\bin\main7c.rs:15:10
|
15 | fn next(&mut self) -> Option<Self::Item> {
| ^^^^^^^^^
note: ...so that reference does not outlive borrowed content
--> src\bin\main7c.rs:16:9
|
16 | Some(&self.holder.data.borrow()[0])
| ^^^^^^^^^^^^^^^^
note: but, the lifetime must be valid for the lifetime `'a` as defined here...
--> src\bin\main7c.rs:12:6
|
12 | impl<'a, D: Borrow<[u8]>, T> Iterator for MyIterator<'a, D, T> {
| ^^
note: ...so that the types are compatible
--> src\bin\main7c.rs:15:43
|
15 | fn next(&mut self) -> Option<Self::Item> {
| ______________________________________________^
16 | | Some(&self.holder.data.borrow()[0])
17 | | }
| |_____^
= note: expected `<MyIterator<'a, D, T> as Iterator>`
found `<MyIterator<'_, D, T> as Iterator>`
The compiler can't infer that the lifetime of the returned value must be 'a
like in the first example. I could solve this giving up on implementing Iterator
but that would defeat the whole point of using an iterator.
impl<'a, D: Borrow<[u8]>, T> MyIterator<'a, D, T> {
fn next(&'a mut self) -> Option<&'a u8> {
Some(&self.holder.data.borrow()[0])
}
}
Is there a way of solving this without giving up on the Iterator
trait?
Is there a way of solving this without giving up on the
Iterator
trait?
No. Suppose D
is Vec<u8>
. What would be the lifetime 'a
?
When it was already a reference, we could just reuse its lifetime. This is because, assuming data
is &'a T
, &self.data[0]
where self
is &'b mut self
can be seen as (pseudo-Rust):
let slice_data_pointer: &'a u8 = (*self.data);
slice_data_pointer
That is, we just copy the reference.
But when data
is owned, there is no reference to be copied. The only source of truth is self.data
itself - and self.data
is borrowed only for the lifetime of self
. So you need the yielded values of iterator to depend on next()
's self
. This is the classic lending/streaming iterator, and it:
self
with different lifetimes. In other words, it cannot implement operations like collect()
. However, we can learn from the way the actual Vec
impls Iterator
. Specifically, it doesn't - and so do owned values in general - they implement IntoIterator
instead for a reference to them, potentially with an iter()
method.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.