简体   繁体   English

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

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

I'm looking for a method that consumes a Vec and returns one element, without the overhead of restoring Vec 's invariants the way remove and swap_remove do:我正在寻找一种使用Vec并返回一个元素的方法,而无需像removeswap_remove那样恢复Vec的不变量的swap_remove

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

However, I can't find such a method.但是,我找不到这样的方法。 Am I missing something?我错过了什么吗? Is this actually unsafe or impossible?这实际上是不安全的还是不可能的?

This is a different question from Built in *safe* way to move out of Vec<T>?这与Built in *safe* way to move out of Vec<T>?的问题不同。 There the goal was a remove method that didn't panic on out of bounds access and returned a Result .那里的目标是一个remove方法,它不会在越界访问时恐慌并返回一个Result I'm looking for a method that consumes a Vec and returns one of the elements.我正在寻找一种使用Vec并返回其中一个元素的方法。 None of the answers to the above question address my question.上述问题的答案都没有解决我的问题。

You can write your function like this: 你可以写这样的函数:

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

This is guaranteed O(1). 这是保证O(1)。


To mention another solution using iterators: 提到使用迭代器的另一种解决方案:

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

I was about to write this: 我正要写这个:

While Iterator::nth() usually is a linear time operation, the iterator over a vector overrides this method to make it a O(1) operation. 虽然Iterator::nth()通常是线性时间操作,但是向量上的迭代器会覆盖此方法以使其成为O(1)操作。

But then I noticed, that this is only true for the iterator which iterates over slices. 但后来我注意到,这只适用于迭代切片的迭代器。 The std::vec::IntoIter iterator which would be used in the code above, doesn't override nth() . 将在上面的代码中使用的std::vec::IntoIter迭代器不会覆盖nth() It has been attempted here , but it doesn't seem to be that easy. 这里已经尝试 ,但似乎并不那么容易。

So : as of right now, the code above is a O(n) operation! 所以 :截至目前,上面的代码是O(n)操作!

The reason fn take<T>(vec: Vec<T>, index: usize) -> Option<T> does not exist in the standard library, is that it is in general not very useful. fn take<T>(vec: Vec<T>, index: usize) -> Option<T>在标准库中是不存在的,因为它通常不是很有用。 For example, supposing that you have a Vec<String> of length 10, it means throwing away 9 strings and only use 1. This seems wasteful. 例如,假设您的长度为10的Vec<String> ,则意味着丢掉9个字符串并仅使用1.这看起来很浪费。

In general, the standard library will try to provide an API that is useful in a maximum of scenarios, and in this instance it would be more logical to have a fn take<T>(vec: &mut Vec<T>, index: usize) -> Option<T> . 通常,标准库将尝试提供在最多场景中有用的API,在这种情况下,使用fn take<T>(vec: &mut Vec<T>, index: usize) -> Option<T>更合乎逻辑fn take<T>(vec: &mut Vec<T>, index: usize) -> Option<T>

The only question is how to preserve the invariant, of course: 唯一的问题是如何保留不变量,当然:

  • it can be preserved by exchanging with the last element, which is what Vec::swap_remove does, 它可以通过交换最后一个元素来保存,这就是Vec::swap_remove所做的,
  • it can be preserved by shifting the successor elements in, which is what Vec::drain does. 它可以通过移动后继元素来保存,这就是Vec::drain所做的。

Those are very flexible, and can be adapted to fill more specific scenarios, such as yours. 这些非常灵活,可以适应更多特定情况,例如你的。


Adapting swap_remove : 适应swap_remove

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

Adapting drain : 适应drain

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

Noting that the former is more efficient: it's O(1). 注意前者更有效:它是O(1)。


To clarify further, 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. 为了进一步澄清,我正在寻找一种消耗Vec并返回一个元素的方法,而不需要像removeswap_remove那样恢复Vec不变量的开销。

This reeks of premature micro-optimization to me. 这对我来说是过早的微观优化。

First of all, note that it is necessary to destroy the elements of the vector; 首先,请注意有必要销毁向量的元素; you can accomplish this in two ways: 你可以通过两种方式实现这一目标:

  1. swap_remove , then iterate over each element to destroy them, swap_remove ,然后迭代每个元素来销毁它们,
  2. Iterate over each element to destroy them, skipping the specific index . 迭代每个元素以销毁它们,跳过特定index

It is not clear to me that the latter would be faster than the former; 我不清楚后者会比前者更快; if anything it looks more complicated, with more branches (I advise two loops), which may throw off the predictor and may be less amenable to vectorization. 如果它看起来更复杂,有更多的分支(我建议两个循环),这可能会抛弃预测器,可能不太适合矢量化。

Secondly, before complaining about the overhead of restoring the Vec 's invariant, have you properly profiled the solution? 其次,在抱怨恢复Vec不变量的开销之前,你有没有正确分析解决方案?

If we look at the swap_remove variant, there are 3 steps: 如果我们查看swap_remove变体,有3个步骤:

  1. swap_remove (O(1)), swap_remove (O(1)),
  2. destroy each remaining element (O(N)), 破坏每个剩余的元素(O(N)),
  3. free the backing memory. 释放后备记忆。

Step 2 may be optimized out if the element has no Drop implementation, but otherwise I would be it's a toss whether (2) or (3) is dominating the cost. 如果元素没有Drop实现,则可以优化步骤2,但是否则(2)或(3)是否支配成本,这将是一个折腾。

TL;DR : I am afraid that you are fighting ghost issues, profile before trying to optimize. TL; DR:恐怕你正在设法优化前战斗鬼的问题, 个人资料

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM