0

Not clearly a duplicate of the linked question

This question is about returning a reference to the member of an owned struct, not about storing a reference to an owned value on the same object.

I'm hoping for some explanation, or some hints towards what is happening in the very specific case.


I'm using the streaming iterators library to build runtime pipelines where the exact types of iterators are not known at compile time. For example, I want to construct a pipeline like

read a file > gunzip it > parse the contents > extract fields

To do this efficiently, borrowing slices across the iterators seems important. My best attempt at creating an iterator that gzip decompresses [u8]s passed in by another iterator is in a playground.

The error produced is:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:48:50
   |
48 |         self.reader = gzip::Decoder::new(self.it.next().unwrap()).ok();
   |                                                  ^^^^
   |
note: first, the lifetime cannot outlive the anonymous lifetime #1 defined on the method body at 47:5...
  --> src/main.rs:47:5
   |
47 | /     fn advance(&mut self) { 
48 | |         self.reader = gzip::Decoder::new(self.it.next().unwrap()).ok();
49 | |     }
   | |_____^
note: ...so that reference does not outlive borrowed content
  --> src/main.rs:48:42
   |
48 |         self.reader = gzip::Decoder::new(self.it.next().unwrap()).ok();
   |                                          ^^^^^^^
note: but, the lifetime must be valid for the lifetime 'a as defined on the impl at 44:1...
  --> src/main.rs:44:1
   |
44 | / impl<'a> StreamingIterator for GunzipIter<'a> {
45 | |     type Item = gzip::Decoder<&'a [u8]>;
46 | |     
47 | |     fn advance(&mut self) { 
...  |
50 | |     fn get(&self) -> Option<&gzip::Decoder<&'a [u8]>> { Some(&(self.reader.unwrap())) }
51 | | }
   | |_^
note: ...so that expression is assignable (expected std::option::Option<gzip::Decoder<&'a [u8]>>, found std::option::Option<gzip::Decoder<&[u8]>>)
  --> src/main.rs:48:23
   |
48 |         self.reader = gzip::Decoder::new(self.it.next().unwrap()).ok();
   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

There's an example from the library:

use streaming_iterator::StreamingIterator;

pub struct FakeStreamingIterator<'a, I, T>
where
    I: Iterator<Item = &'a &'a T>,
    T: 'a + ?Sized,
{
    inner: Box<I>,
    current: Option<&'a &'a T>,
}

impl<'a, I, T> FakeStreamingIterator<'a, I, T>
where
    I: Iterator<Item = &'a &'a T>,
    T: ?Sized,
{
    fn new(inner: I) -> FakeStreamingIterator<'a, I, T> {
        FakeStreamingIterator {
            inner: Box::new(inner),
            current: None,
        }
    }
}

impl<'a, I, T> StreamingIterator for FakeStreamingIterator<'a, I, T>
where
    I: Iterator<Item = &'a &'a T>,
    T: ?Sized,
{
    type Item = T;

    fn advance(&mut self) {
        self.current = self.inner.next();
    }
    fn get(&self) -> Option<&T> {
        self.current.map(|x| *x)
    }
}

That compiles fine, but a similar example that more closely matches my use-case doesn't:

use streaming_iterator::StreamingIterator;

pub struct FakeStreamingIterator<'a> {
    inner: Box<StreamingIterator<Item = [u8]>>,
    current: Option<&'a [u8]>,
}

impl<'a> FakeStreamingIterator<'a> {
    fn new(inner: Box<StreamingIterator<Item = [u8]>>) -> FakeStreamingIterator<'a> {
        FakeStreamingIterator {
            inner: inner,
            current: None,
        }
    }
}

impl<'a> StreamingIterator for FakeStreamingIterator<'a> {
    type Item = [u8];

    fn advance(&mut self) {
        self.current = self.inner.next();
    }
    fn get(&self) -> Option<&[u8]> {
        self.current.map(|x| x)
    }
}

The error always comes down to lifetime mismatches, but I don't understand how to resolve this. The main differences, as I can see are:

  • I'm holding a Box of a trait, rather than a Box of a type. This is needed because the parent iterator is not known at compile time.
  • I'm using a slice rather than a reference to a reference. I don't understand why that would affect lifetimes.

In my mind, there are two logical lifetimes in play:

  1. The lifetime of the iterator
  2. The lifetime of the current slice.

The slice lifetime must be shorter than the iterator lifetime. I'm assuming there's a situation where Rust can guarantee that advance(&mut) isn't called while there's an active borrow from the result of get(&) alive, but am not sure how this can be written.

I've read a lot about borrows and lifetimes, but don't understand the difference in this situation.

stestagg
  • 81
  • 5
  • 1
    Not really an answer, but a possibility would be to use a memory pool with fixed size buffers and pass the buffer around. Pre-allocate X buffers, and never allocate again ever. – Matthieu M. Jan 03 '18 at 15:28
  • I'm having trouble understanding the issue without a more concrete and compilable example. – NovaDenizen Jan 03 '18 at 16:56
  • @NovaDenizen - Sorry, I had attempted to simplify the problem above by providing a similar example that does compile. A more relevant sample that shows the same compiler error is here: https://play.rust-lang.org/?gist=ea3ca0a6591eabb1b74ae75f1317085e&version=stable – stestagg Jan 03 '18 at 17:46
  • Highly likely to be a duplicate of [Why can't I store a value and a reference to that value in the same struct?](https://stackoverflow.com/q/32300132/155423). You have a single struct containing both `Box` and a `gzip::Decoder`. The iterator returns references to itself and you try to keep them in the decoder; thus trying to store a reference to itself inside of itself. – Shepmaster Jan 03 '18 at 18:38
  • 1
    Well, possibly, but it's very unclear how. The intent is to borrow a reference from the Iterator, and store a decoder that uses that borrowed reference, allowing a reference to that decoder to be borrowed on. The iterator returns references to a slice, which shouldn't, as I understand, it be tied to the iterator instance – stestagg Jan 03 '18 at 18:50

0 Answers0