我期待Vec::insert_slice(index, slice)
方法 - 字符串(String::insert_str()
)的解决方案确实存在。
我知道Vec::insert()
,但是一次只插入一个元素,而不是切片。或者,当前置切片是Vec
时,可以将其附加到它,但这不会概括。惯用解决方案可能使用Vec::splice()
,但在示例中使用迭代器让我抓狂。
其次,预先支出的整个概念似乎已从docs中驱除。没有一个提及。我很感激为什么评论。请注意,确实存在像Vec::swap_remove()
这样相对模糊的方法。
我的典型用例包括索引字节字符串。
答案 0 :(得分:4)
String::insert_str
利用字符串本质上是Vec<u8>
的事实。它reallocates the underlying buffer, moves all the initial bytes to the end, then adds the new bytes to the beginning。
这通常不安全,无法直接添加到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
's implementation is non-trivial,但它有效地完成了我们需要的跟踪。删除一大块值后,重新重新该内存块以获取新值。它还会移动向量的尾部(可能是几次,具体取决于输入迭代器)。 Drop
的{{1}}实现可确保事物始终处于有效状态。
我更感到惊讶的是VecDeque
不支持它,因为它旨在更有效地修改数据的头部和尾部。
答案 1 :(得分:1)
考虑到Shepmaster所说的内容,您可以通过以下方式实现一个函数,将具有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]);
}