Как добавить ломтик к Vec
Я ожидал Vec::insert_slice(index, slice)
метод - решение для строк (String::insert_str()
) существует.
Я знаю о Vec::insert()
, но это вставляет только один элемент за раз, а не срез. Альтернативно, когда предварительно срезанный срез представляет собой Vec
вместо этого можно добавить к нему, но это не обобщает. Идиоматическое решение, вероятно, использует Vec::splice()
, но использование итераторов, как в примере, заставляет меня почесать голову.
Во-вторых, вся концепция предвидения, по-видимому, была изгнана из документов. Там нет ни одного упоминания. Буду признателен за комментарии о том, почему. Обратите внимание, что относительно малоизвестные методы, такие как Vec::swap_remove()
существуют
Мой типичный вариант использования состоит из индексированных байтовых строк.
2 ответа
String::insert_str
использует тот факт, что строка по существу Vec<u8>
, Он перераспределяет базовый буфер, перемещает все начальные байты в конец, затем добавляет новые байты в начало.
Это не всегда безопасно и не может быть добавлено непосредственно в Vec
потому что во время копирования Vec
больше не находится в допустимом состоянии - в данных есть "дыры".
Это не имеет значения для String
потому что данные u8
а также u8
не реализует Drop
, Там нет такой гарантии для произвольного T
в Vec
, но если вы очень внимательно следите за своим состоянием и убираетесь должным образом, вы можете сделать то же самое - вот что splice
делает!
вся концепция предвидения, казалось бы, была изгнана
Я бы предположил, что это потому, что готовится к Vec
плохая идея с точки зрения производительности. Если вам нужно сделать это, наивный случай прост:
fn prepend<T>(v: Vec<T>, s: &[T]) -> Vec<T>
where
T: Clone,
{
let mut tmp: Vec<_> = s.to_owned();
tmp.extend(v);
tmp
}
Это занимает немного больше памяти, так как нам нужно достаточно места для двух копий v
,
splice
Метод принимает итератор новых значений и диапазон значений для замены. В этом случае мы не хотим ничего заменять, поэтому мы даем пустой диапазон индекса, в который мы хотим вставить. Нам также нужно преобразовать фрагмент в итератор соответствующего типа:
let s = &[1, 2, 3];
let mut v = vec![4, 5];
v.splice(0..0, s.iter().cloned());
splice
Реализация не является тривиальной, но она эффективно делает отслеживание, в котором мы нуждаемся. После удаления фрагмента значений он затем использует этот фрагмент памяти для новых значений. Он также перемещает хвост вектора (возможно, несколько раз, в зависимости от входного итератора). Drop
реализация Slice
гарантирует, что вещи всегда будут в действительном состоянии.
Я больше удивлен, что VecDeque
не поддерживает его, так как он предназначен для более эффективной модификации как заголовка, так и хвоста данных.
Принимая во внимание то, что сказал Шепмастер, вы можете реализовать функцию, добавляющую срез к Copy
способные элементы к Vec
как String::insert_str()
делает следующим образом:
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]);
}