简体   繁体   中英

Rust - Specifying a Lifetime Parameter for the Borrow Trait

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:

  • The 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
  • The compiler is smart enough to detect that since 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:

  • Doesn't exist in the standard library.
  • Requires Generic Associated Types (which is part the reason it is not in the standard library).
  • Allows only one yielded value to exist at any time - they can't coexist, because they all borrow mutably from 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM