如何一次将一个值从数组中移出?
How do I move values out of an array one at a time?
我拥有一个大小为 3 的数组的所有权,我想对其进行迭代,同时将元素移出。基本上,我想为固定大小的数组实现 IntoIterator
。
由于数组没有在标准库中实现这个特性(我明白为什么),是否有解决方法来获得预期的效果?我的对象不是 Copy
也不是 Clone
。我可以从数组创建一个 Vec
,然后迭代到 Vec
,但我什至不知道该怎么做。
(有关信息,我想完成 Complete
的数组)
这里有一个简单的例子(天真iter()
尝试):
// No-copy, No-clone struct
#[derive(Debug)]
struct Foo;
// A method that needs an owned Foo
fn bar(foo: Foo) {
println!("{:?}", foo);
}
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
for a in v.iter() {
bar(*a);
}
}
给予
error[E0507]: cannot move out of borrowed content
--> src/main.rs:14:13
|
14 | bar(*a);
| ^^ cannot move out of borrowed content
Rust 2021(可从 Rust 1.56 获得)
您可以使用 for 循环迭代数组:
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
for a in v {
bar(a);
}
}
struct Foo;
fn bar(_: Foo) {}
生锈 1.51
您可以使用 std::array::IntoIter
获取按值数组迭代器:
use std::array::IntoIter;
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
for a in IntoIter::new(v) {
bar(a);
}
}
struct Foo;
fn bar(_: Foo) {}
以前的 Rust 版本
您需要的核心是在不移动数组的情况下从数组中获取值。
这可以使用 mem::transmute
to convert the array to an array of mem::MaybeUninit
, then using ptr::read
将值保留在数组中但取回拥有的值来完成:
let one = unsafe {
let v = mem::transmute::<_, [MaybeUninit<Foo>; 3]>(v);
ptr::read(&v[0]).assume_init()
};
bar(one);
只需循环执行几次即可。
只有一个小问题:你看到了吗unsafe
?你猜到了;在更广泛的情况下,这完全、可怕地被打破了:
MaybeUninit
掉落时什么都不做;这可能会导致内存泄漏。
- 如果在移出值的过程中发生恐慌(例如
bar
函数中的某处),数组将处于部分未初始化状态。这是另一个可以删除 MaybeUninit
的(微妙的)路径,所以现在我们必须知道数组仍然拥有哪些值,哪些值已被移出。我们有责任释放我们仍然拥有的价值,而不是其他价值。
- 没有什么能阻止我们自己意外访问数组中新近失效的值。
正确的解决方案是跟踪数组中有多少值有效/无效。删除数组时,您可以删除剩余的有效项并忽略无效项。如果我们能让它适用于不同大小的数组,那就太好了...
这就是 arrayvec 的用武之地。它没有 与 完全相同的实现(因为它更智能),但它确实具有相同的语义:
use arrayvec::ArrayVec; // 0.5.2
#[derive(Debug)]
struct Foo;
fn bar(foo: Foo) {
println!("{:?}", foo)
}
fn main() {
let v = ArrayVec::from([Foo, Foo, Foo]);
for f in v {
bar(f);
}
}
您可以使用 Option<Foo>
数组代替 Foo
数组。它当然有一些内存损失。函数 take()
将数组中的值替换为 None
.
#[derive(Debug)]
struct Foo;
// A method that needs an owned Foo
fn bar(foo: Foo) { println!("{:?}", foo); }
fn main() {
let mut v = [Some(Foo),Some(Foo),Some(Foo)];
for a in &mut v {
a.take().map(|x| bar(x));
}
}
使用non-lexical lifetimes feature (available since Rust 1.31.0) and a fixed-length slice pattern (available since Rust 1.26.0)你可以移出一个数组:
#[derive(Debug)]
struct Foo;
fn bar(foo: Foo) {
println!("{:?}", foo);
}
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
let [a, b, c] = v;
bar(a);
bar(b);
bar(c);
}
但是,如果数组很大,则此解决方案无法很好地扩展。
如果您不介意额外分配,另一种方法是将数组装箱并将其转换为 Vec
:
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
let v = Vec::from(Box::new(v) as Box<[_]>);
for a in v {
bar(a);
}
}
如果数组很大,那可能是个问题。但是,如果数组很大,你不应该首先在堆栈中创建它!
我拥有一个大小为 3 的数组的所有权,我想对其进行迭代,同时将元素移出。基本上,我想为固定大小的数组实现 IntoIterator
。
由于数组没有在标准库中实现这个特性(我明白为什么),是否有解决方法来获得预期的效果?我的对象不是 Copy
也不是 Clone
。我可以从数组创建一个 Vec
,然后迭代到 Vec
,但我什至不知道该怎么做。
(有关信息,我想完成 Complete
的数组)
这里有一个简单的例子(天真iter()
尝试):
// No-copy, No-clone struct
#[derive(Debug)]
struct Foo;
// A method that needs an owned Foo
fn bar(foo: Foo) {
println!("{:?}", foo);
}
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
for a in v.iter() {
bar(*a);
}
}
给予
error[E0507]: cannot move out of borrowed content
--> src/main.rs:14:13
|
14 | bar(*a);
| ^^ cannot move out of borrowed content
Rust 2021(可从 Rust 1.56 获得)
您可以使用 for 循环迭代数组:
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
for a in v {
bar(a);
}
}
struct Foo;
fn bar(_: Foo) {}
生锈 1.51
您可以使用 std::array::IntoIter
获取按值数组迭代器:
use std::array::IntoIter;
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
for a in IntoIter::new(v) {
bar(a);
}
}
struct Foo;
fn bar(_: Foo) {}
以前的 Rust 版本
您需要的核心是在不移动数组的情况下从数组中获取值。
这可以使用 mem::transmute
to convert the array to an array of mem::MaybeUninit
, then using ptr::read
将值保留在数组中但取回拥有的值来完成:
let one = unsafe {
let v = mem::transmute::<_, [MaybeUninit<Foo>; 3]>(v);
ptr::read(&v[0]).assume_init()
};
bar(one);
只需循环执行几次即可。
只有一个小问题:你看到了吗unsafe
?你猜到了;在更广泛的情况下,这完全、可怕地被打破了:
MaybeUninit
掉落时什么都不做;这可能会导致内存泄漏。- 如果在移出值的过程中发生恐慌(例如
bar
函数中的某处),数组将处于部分未初始化状态。这是另一个可以删除MaybeUninit
的(微妙的)路径,所以现在我们必须知道数组仍然拥有哪些值,哪些值已被移出。我们有责任释放我们仍然拥有的价值,而不是其他价值。 - 没有什么能阻止我们自己意外访问数组中新近失效的值。
正确的解决方案是跟踪数组中有多少值有效/无效。删除数组时,您可以删除剩余的有效项并忽略无效项。如果我们能让它适用于不同大小的数组,那就太好了...
这就是 arrayvec 的用武之地。它没有 与 完全相同的实现(因为它更智能),但它确实具有相同的语义:
use arrayvec::ArrayVec; // 0.5.2
#[derive(Debug)]
struct Foo;
fn bar(foo: Foo) {
println!("{:?}", foo)
}
fn main() {
let v = ArrayVec::from([Foo, Foo, Foo]);
for f in v {
bar(f);
}
}
您可以使用 Option<Foo>
数组代替 Foo
数组。它当然有一些内存损失。函数 take()
将数组中的值替换为 None
.
#[derive(Debug)]
struct Foo;
// A method that needs an owned Foo
fn bar(foo: Foo) { println!("{:?}", foo); }
fn main() {
let mut v = [Some(Foo),Some(Foo),Some(Foo)];
for a in &mut v {
a.take().map(|x| bar(x));
}
}
使用non-lexical lifetimes feature (available since Rust 1.31.0) and a fixed-length slice pattern (available since Rust 1.26.0)你可以移出一个数组:
#[derive(Debug)]
struct Foo;
fn bar(foo: Foo) {
println!("{:?}", foo);
}
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
let [a, b, c] = v;
bar(a);
bar(b);
bar(c);
}
但是,如果数组很大,则此解决方案无法很好地扩展。
如果您不介意额外分配,另一种方法是将数组装箱并将其转换为 Vec
:
fn main() {
let v: [Foo; 3] = [Foo, Foo, Foo];
let v = Vec::from(Box::new(v) as Box<[_]>);
for a in v {
bar(a);
}
}
如果数组很大,那可能是个问题。但是,如果数组很大,你不应该首先在堆栈中创建它!