I was expecting a Vec::insert_slice(index, slice)
method — a solution for strings (String::insert_str()
) does exist.
I know about Vec::insert()
, but that inserts only one element at a time, not a slice. Alternatively, when the prepended slice is a Vec
one can append to it instead, but this does not generalize. The idiomatic solution probably uses Vec::splice()
, but using iterators as in the example makes me scratch my head.
Secondly, the whole concept of prepending has seemingly been exorcised from the docs. There isn't a single mention. I would appreciate comments as to why. Note that relatively obscure methods like Vec::swap_remove()
do exist.
My typical use case consists of indexed byte strings.
Vector is a module in Rust that provides the container space to store values. It is a contiguous resizable array type, with heap-allocated contents. It is denoted by Vec<T>. Vectors in Rust have O(1) indexing and push and pop operations in vector also take O(1) complexity.
In Rust, there are several ways to initialize a vector. In order to initialize a vector via the new() method call, we use the double colon operator: let mut vec = Vec::new();
To remove all elements from a vector in Rust, use . retain() method to keep all elements the do not match. let mut v = vec![
String::insert_str
makes use of the fact that a string is essentially a Vec<u8>
. It reallocates the underlying buffer, moves all the initial bytes to the end, then adds the new bytes to the beginning.
This is not generally safe and can not be directly added to Vec
because during the copy the Vec
is no longer in a valid state — there are "holes" in the data.
This doesn't matter for String
because the data is u8
and u8
doesn't implement Drop
. There's no such guarantee for an arbitrary T
in a Vec
, but if you are very careful to track your state and clean up properly, you can do the same thing — this is what splice
does!
the whole concept of prepending has seemingly been exorcised
I'd suppose this is because prepending to a Vec
is a poor idea from a performance standpoint. If you need to do it, the naïve case is straight-forward:
fn prepend<T>(v: Vec<T>, s: &[T]) -> Vec<T>
where
T: Clone,
{
let mut tmp: Vec<_> = s.to_owned();
tmp.extend(v);
tmp
}
This has a bit higher memory usage as we need to have enough space for two copies of v
.
The splice
method accepts an iterator of new values and a range of values to replace. In this case, we don't want to replace anything, so we give an empty range of the index we want to insert at. We also need to convert the slice into an iterator of the appropriate type:
let s = &[1, 2, 3];
let mut v = vec![4, 5];
v.splice(0..0, s.iter().cloned());
splice
's implementation is non-trivial, but it efficiently does the tracking we need. After removing a chunk of values, it then reuses that chunk of memory for the new values. It also moves the tail of the vector around (maybe a few times, depending on the input iterator). The Drop
implementation of Slice
ensures that things will always be in a valid state.
I'm more surprised that VecDeque
doesn't support it, as it's designed to be more efficient about modifying both the head and tail of the data.
Taking into consideration what Shepmaster said, you could implement a function prepending a slice with Copy
able elements to a Vec
just like String::insert_str()
does in the following way:
use std::ptr;
unsafe fn prepend_slice<T: Copy>(vec: &mut Vec<T>, slice: &[T]) {
let len = vec.len();
let amt = slice.len();
vec.reserve(amt);
ptr::copy(vec.as_ptr(),
vec.as_mut_ptr().offset((amt) as isize),
len);
ptr::copy(slice.as_ptr(),
vec.as_mut_ptr(),
amt);
vec.set_len(len + amt);
}
fn main() {
let mut v = vec![4, 5, 6];
unsafe { prepend_slice(&mut v, &[1, 2, 3]) }
assert_eq!(&v, &[1, 2, 3, 4, 5, 6]);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With