初始化盒装切片,无需克隆或复制

Cod*_*ody 1 rust

我正在尝试初始化一个装箱的None值片段,以便基础类型T不需要实现Cloneor Copy。这里有一些理想的解决方案:

fn by_vec<T>() -> Box<[Option<T>]> {
    vec![None; 5].into_boxed_slice()
}

fn by_arr<T>() -> Box<[Option<T>]> {
    Box::new([None; 5])
}
Run Code Online (Sandbox Code Playgroud)

不幸的是,by_vec实施需要T: Cloneby_arr实施也需要T: Copy。我尝试了更多方法:

fn by_vec2<T>() -> Box<[Option<T>]> {
    let v = &mut Vec::with_capacity(5);
    for i in 0..v.len() {
        v[i] = None;
    }
    v.into_boxed_slice() // Doesn't work: cannot move out of borrowed content
}

fn by_iter<T>() -> Box<[Option<T>]> {
     (0..5).map(|_| None).collect::<Vec<Option<T>>>().into_boxed_slice()
}
Run Code Online (Sandbox Code Playgroud)

by_vec2没有通过编译器(我不确定我理解为什么),但是by_iter可以。我担心的性能collect- 它是否需要在迭代时调整它所收集的向量的大小,或者它是否可以首先分配正确大小的向量?

也许我的想法是错误的——我对 Rust 很陌生,所以任何建议将不胜感激!

oli*_*obk 5

让我们从 开始by_vec2。您正在引用&muta Vec。您不应该这样做,直接使用Vec并使v绑定可变。

然后,您将迭代Vec容量为 5、长度为 0 的 a 的长度。这意味着您的循环永远不会被执行。你想要的是迭代0..v.cap()

由于您的v长度仍然为0,因此v[i]在循环中访问将在运行时出现恐慌。你真正想要的是v.push(None)。这通常会导致重新分配,但在您的情况下,您已经分配了Vec::with_capacity,因此推送 5 次将不会分配。

这次我们没有引用所以Vec实际上into_boxed_slice会起作用。

fn by_vec2<T>() -> Box<[Option<T>]> {
    let mut v = Vec::with_capacity(5);
    for _ in 0..v.capacity() {
        v.push(None);
    }
    v.into_boxed_slice()
}
Run Code Online (Sandbox Code Playgroud)

您的by_iter函数实际上只分配一次。创建的 Range 迭代器0..5知道它正好有 5 个元素长。因此collect实际上会检查该长度并仅分配一次。

  • 我认为这里最重要的是 *Range 迭代器 [...] 知道它正好是 5 个元素长*。这就是我编写这段代码的方式,尽管我会在 `collect` 类型参数上使用类型推断: `(0..5).map(|_| None).collect::&lt;Vec&lt;_&gt;&gt;( ).into_boxed_slice()`。 (3认同)