David Beck
David Beck

Reputation: 361

How to express lifetime for Rust iterator for a container

I have a circular buffer like this:

struct CircularBuffer<T: Copy> {
    seqno: usize,
    data: Vec<T>,
}

And I want to create an external struct being an iterator. This struct would refer to the internal data vector of the CircularBuffer like this one:

struct CircularBufferIterator<'a, T: 'a + Copy> {
    buffer: &'a CircularBuffer<T>,
    position: usize,
    limit: usize,
}

This is the best I could come up with that actually compiles. Can you please suggest a better way to express that the CircularBufferIterator depends on the CircularBuffer object?

What troubles me is T: 'a + Copy. I wonder if it is possible or it makes sense to say that not the T type, but CircularBuffer<T> is the one CircularBufferIterator depends on.

The part I don't see is why do I need to add the 'a lifetime to T. Cannot that be T: Copy, without a lifetime? In other words, I cannot see a case when T reference outlives the CircularBuffer. It is the CircularBuffer reference that outlives the CircularBufferIterator.

The CircularBuffer and the context comes from this blog post.

Upvotes: 2

Views: 704

Answers (1)

Shepmaster
Shepmaster

Reputation: 430554

why do I need to add the 'a lifetime to T

You aren't adding a lifetime to T; you are saying that whatever T is chosen, it can only contain references that outlive 'a. If that wasn't the case, then we might have a reference to a type that has a reference that is now invalid. Using that invalid reference would lead to memory unsafety; a key thing that Rust seeks to avoid.


I originally thought you were asking how to remove the Copy bound, so here's all that I typed up.

One change would be to remove the Copy bound from CircularBuffer but leaving it on the implementation of the methods. Then you don't need it on the iterator at all:

struct CircularBuffer<T> {
    seqno: usize,
    data: Vec<T>,
}

struct CircularBufferIterator<'a, T: 'a> {
    buffer: &'a CircularBuffer<T>,
    position: usize,
    limit: usize,
}

Another change would be to completely eschew the direct reference to the CircularBuffer altogether, and keep direct iterators into the Vec:

struct CircularBufferIterator<'a, T: 'a> {
    first: std::slice::Iter<'a, T>,
    second: Option<std::slice::Iter<'a, T>>,
}

However, looking at the Iterator implementation, I see it returns a T, not a &T, so you ultimately need a type that is Copy or Clone. You'll note that the standard library doesn't require this because it returns a reference to the item in the collection. If you do need a non-reference, that's what into_iter or Iterator::cloned is for.

Upvotes: 3

Related Questions