Reputation: 769
I have a type with a VecDeque<T>
field and a get() -> Option<&Vec<T>>
method:
struct Storage<T> {
buffer: VecDeque<T>
}
impl<T> Storage<T> {
fn get(&self, /* args */ ) -> Option<&Vec<T>> { /* ... */ }
}
Based on the input arguments to get()
I would like to return an Option<&Vec<T>>
that essentially refers to the slice from index i
to index j
inside the buffer
:
index = 0 ... i ... j ... (n-1)
buffer = [a, b, c, d, e, f, g, h]
|-----|
return &Vec<T>
In my attempts so far I see that VecDeque::as_slices()
might help (I ensure the buffer is always contiguous before get()
is called) and I also see that there are ways to convert slices into a Vec<T>
. Its the reference part &Vec<T>
that I seem to be having trouble with.
Additional Details: The get()
method actually comes from the implementation of a trait:
trait Storable {
type OutputType;
fn get(&self, /* args */) -> Option<&Self::OutputType>;
}
This API is following that of the Slab
type from the slab crate and, in fact, implementing Storable
for Slab
is very straightforward. One possibility is that I change the OutputType
associated type from Vec<T>
to something else. However, elsewhere in my code I might be relying heavily on the assumption that this associated type is a Vec<T>
. That's probably a code smell. Whatever the solution might be, I want to avoid copy/clones as much as possible for performance reasons.
Upvotes: 1
Views: 1084
Reputation: 9647
I believe this isn't possible in the way you imagine it to happen, because of ownership rules:
If you get your deque as slices, then you'll get your single contiguous slice, which is essentially an immutable borrow. That means the slice doesn't own the values. Its type will be &[T]
.
Now a Vec<T>
on the other hand will want to own the values of type T. So creating a Vec<T>
from a &[T]
will have to involve either a copy or a clone, depending on whether T
supports either of those operations.
The fact that you want a &Vec<T>
doesn't change the fact that somewhere you'd need to create the actual Vec<T>
on the heap, which in turns requires changing ownership.
Another issue with returning &Vec<T>
is that of lifetime. If this Vec
is freshly created, through whatever conversion you figure out works for you, it'll be only valid as long as it lives, which would be the get
function:
impl<T> Storage<T> {
fn get(&self, /* args*/ ) -> Option<&Vec<T>> {
let return_vec: Vec<T> = /* some code */;
/* some more code */
Ok(&return_vec); // Uh oh!
}
}
The compiler will complain that you can't return a reference to something that's local in a function.
I think the best option is to just change the output type to be a slice, &[T]
. You say that in your own internal code you rely heavily on the vec representation. Now the nice thing is that via some smart trait implementations, any functions that expects &[T]
as an argument will also just work for Vec<T>
.
Upvotes: 3