我如何从 Rust 中的 Vec 中取出一个项目?

How can I take an item from a Vec in Rust?

我正在寻找一种消耗一个Vec和returns一个元素的方法,没有恢复Vec的开销' s 不变式 removeswap_remove 的方式:

fn take<T>(vec: Vec<T>, index: usize) -> Option<T>

但是,我找不到这样的方法。我错过了什么吗?这实际上是不安全的还是不可能的?

这是一个不同于 Built in *safe* way to move out of Vec<T>? 的问题 那里的目标是 remove 方法,它不会在越界访问时恐慌并返回 Result。我正在寻找一种使用 Vec 和 returns 元素之一的方法。上述问题的 None 个答案解决了我的问题。

你可以这样写你的函数:

fn take<T>(mut vec: Vec<T>, index: usize) -> Option<T> {
    if vec.get(index).is_none() {
        None
    } else {
        Some(vec.swap_remove(index))
    }
}

您在此处 看到 的代码(getswap_remove)保证 O(1)。

然而,有点隐藏,vec在函数的末尾被删除,这个删除操作可能不是 O(1),而是 O(n )(其中 n 是 vec.len())。如果 T 实现了 Drop,那么会为向量中的每个元素调用 drop(),这意味着丢弃向量的时间复杂度为 O(n)。如果T没有实现Drop,那么Vec只需要释放内存即可。 dealloc操作的时间复杂度取决于分配器,没有指定,所以我们不能假设它是O(1)。


提及另一个使用迭代器的解决方案:

fn take<T>(vec: Vec<T>, index: usize) -> Option<T> {
    vec.into_iter().nth(index)
}

我正要写这个:

While Iterator::nth() usually is a linear time operation, the iterator over a vector overrides this method to make it a O(1) operation.

但后来我注意到,这仅适用于迭代切片的迭代器。将在上面的代码中使用的 std::vec::IntoIter 迭代器不会覆盖 nth()。已经尝试过here,但似乎没那么容易。

所以,截至目前,上面的迭代器解决方案是一个 O(n) 操作!更不用说删除向量所需的时间了,如上所述。

标准库中不存在fn take<T>(vec: Vec<T>, index: usize) -> Option<T>的原因是一般情况下用处不大。例如,假设你有一个长度为 10 的 Vec<String>,这意味着扔掉 9 个字符串而只使用 1 个。这看起来很浪费。

一般来说,标准库会尝试提供一个 API 在大多数情况下都有用,在这种情况下,使用 fn take<T>(vec: &mut Vec<T>, index: usize) -> Option<T>.[= 会更合乎逻辑。 30=]

唯一的问题是如何保持不变量,当然:

  • 它可以通过与最后一个元素交换来保留,这就是Vec::swap_remove所做的,
  • 它可以通过移入后继元素来保留,这就是 Vec::drain 所做的。

这些非常灵活,可以适应更具体的场景,例如您的场景。


适应swap_remove

fn take<T>(mut vec: Vec<T>, index: usize) -> Option<T> {
    if index < vec.len() {
        Some(vec.swap_remove(index))
    } else {
        None
    }
}

适应drain

fn take<T>(mut vec: Vec<T>, index: usize) -> Option<T> {
    if index < vec.len() {
        vec.drain(index..index+1).next()
    } else {
        None
    }
}

注意到前者效率更高:它是 O(1)。


I'm looking for a method that consumes the Vec and returns one element, without the overhead of restoring Vec's invariants the way remove and swap_remove do.

我觉得这是过早的微优化。

首先要注意的是,需要销毁vector的元素;您可以通过两种方式完成此操作:

  1. swap_remove,然后遍历每个元素销毁它们,
  2. 遍历每个元素以销毁它们,跳过特定的 index

我不清楚后者会比前者快;如果它看起来更复杂,有更多分支(我建议两个循环),这可能会抛出预测器并且可能不太适合矢量化。

其次,在抱怨恢复 Vec 的不变量的开销之前,您是否正确 分析 解决方案?

如果我们查看 swap_remove 变体,有 3 个步骤:

  1. swap_remove (O(1)),
  2. 销毁每个剩余元素 (O(N)),
  3. 释放后备内存。

如果元素没有 Drop 实现,则步骤 2 可能会被优化掉,但否则我会考虑是 (2) 还是 (3) 主导成本。

TL;DR: 恐怕你在打鬼问题,profile 在尝试优化之前。