我如何从 Rust 中的 Vec 中取出一个项目?
How can I take an item from a Vec in Rust?
我正在寻找一种消耗一个Vec
和returns一个元素的方法,没有恢复Vec
的开销' s 不变式 remove
和 swap_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))
}
}
您在此处 看到 的代码(get
和 swap_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的元素;您可以通过两种方式完成此操作:
swap_remove
,然后遍历每个元素销毁它们,
- 遍历每个元素以销毁它们,跳过特定的
index
。
我不清楚后者会比前者快;如果它看起来更复杂,有更多分支(我建议两个循环),这可能会抛出预测器并且可能不太适合矢量化。
其次,在抱怨恢复 Vec
的不变量的开销之前,您是否正确 分析 解决方案?
如果我们查看 swap_remove
变体,有 3 个步骤:
swap_remove
(O(1)),
- 销毁每个剩余元素 (O(N)),
- 释放后备内存。
如果元素没有 Drop
实现,则步骤 2 可能会被优化掉,但否则我会考虑是 (2) 还是 (3) 主导成本。
TL;DR: 恐怕你在打鬼问题,profile 在尝试优化之前。
我正在寻找一种消耗一个Vec
和returns一个元素的方法,没有恢复Vec
的开销' s 不变式 remove
和 swap_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))
}
}
您在此处 看到 的代码(get
和 swap_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 restoringVec
's invariants the wayremove
andswap_remove
do.
我觉得这是过早的微优化。
首先要注意的是,需要销毁vector的元素;您可以通过两种方式完成此操作:
swap_remove
,然后遍历每个元素销毁它们,- 遍历每个元素以销毁它们,跳过特定的
index
。
我不清楚后者会比前者快;如果它看起来更复杂,有更多分支(我建议两个循环),这可能会抛出预测器并且可能不太适合矢量化。
其次,在抱怨恢复 Vec
的不变量的开销之前,您是否正确 分析 解决方案?
如果我们查看 swap_remove
变体,有 3 个步骤:
swap_remove
(O(1)),- 销毁每个剩余元素 (O(N)),
- 释放后备内存。
如果元素没有 Drop
实现,则步骤 2 可能会被优化掉,但否则我会考虑是 (2) 还是 (3) 主导成本。
TL;DR: 恐怕你在打鬼问题,profile 在尝试优化之前。