如何将切片添加到Vec

时间:2017-10-31 14:16:30

标签: rust

我期待Vec::insert_slice(index, slice)方法 - 字符串(String::insert_str())的解决方案确实存在。

我知道Vec::insert(),但是一次只插入一个元素,而不是切片。或者,当前置切片是Vec时,可以将其附加到它,但这不会概括。惯用解决方案可能使用Vec::splice(),但在示例中使用迭代器让我抓狂。

其次,预先支出的整个概念似乎已从docs中驱除。没有一个提及。我很感激为什么评论。请注意,确实存在像Vec::swap_remove()这样相对模糊的方法。

我的典型用例包括索引字节字符串。

2 个答案:

答案 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无关,因为数据为u8u8未实现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]);
}