如何一次一个地将值移出数组? [英] How do I move values out of an array one at a time?

查看:33
本文介绍了如何一次一个地将值移出数组?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我拥有一个大小为 3 的数组,我想对其进行迭代,边走边移动元素.基本上,我希望为固定大小的数组实现 IntoIterator.

I have ownership of an array of size 3 and I would like to iterate on it, moving the elements out as I go. Basically, I would like to have IntoIterator implemented for a fixed-sized array.

由于数组没有在标准库中实现这个特性(我明白为什么),有没有办法获得想要的效果?我的对象不是 Copy 也不是 Clone.我可以从数组中创建一个 Vec,然后迭代到 Vec,但我什至不知道该怎么做.

Since arrays don't implement this trait in the standard library (I understand why), is there a workaround to get the desired effect? My objects are not Copy nor Clone. I'd be okay creating a Vec from the array and then iterating into the Vec, but I'm not even sure how to do that.

(有关信息,我想完成一组 完成)

(For information, I'd like to fulfill an array of Complete)

这是一个简单的情况示例(使用幼稚的 iter() 尝试):

Here is a simple example of the situation (with a naive iter() attempt):

// 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 循环迭代数组:

Rust 2021 (available from Rust 1.56)

You can iterate the array with a for loop:

fn main() {
    let v: [Foo; 3] = [Foo, Foo, Foo];

    for a in v {
        bar(a);
    }
}

struct Foo;
fn bar(_: Foo) {}

Rust 1.51

您可以使用 std::array::IntoIter 得到一个按值的数组迭代器:

Rust 1.51

You can use std::array::IntoIter to get a by-value array iterator:

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 版本

您需要的核心是在不移动数组的情况下从数组中获取值的某种方法.

Previous Rust versions

The core thing you would need is some way of getting the value out of the array without moving it.

这可以使用 mem::transmute 将数组转换为 mem::MaybeUninit,然后使用 ptr::read 将值留在数组中,但返回一个拥有的值:

This can be done using mem::transmute to convert the array to an array of mem::MaybeUninit, then using ptr::read to leave the value in the array but get an owned value back:

let one = unsafe {
    let v = mem::transmute::<_, [MaybeUninit<Foo>; 3]>(v);
    ptr::read(&v[0]).assume_init()
};
bar(one);

只需在循环中执行几次即可,您就可以开始了.

It's just a matter of doing this a few times in a loop and you are good to go.

只有一个小问题:你看到不安全了吗?你猜到了;在更广泛的情况下,这完全是非常糟糕的:

There's just one tiny problem: you see that unsafe? You guessed it; this is totally, horribly broken in the wider case:

  • MaybeUninit 被删除时什么也不做;这可能会导致内存泄漏.
  • 如果在移出值的过程中发生恐慌(例如在 bar 函数内的某处),则数组将处于部分未初始化状态.这是另一个可以删除 MaybeUninit 的(微妙的)路径,所以现在我们必须知道数组仍然拥有哪些值,哪些已被移出.我们有责任释放我们仍然拥有的价值,而不是其他价值.
  • 没有什么能阻止我们自己意外地访问数组中新失效的值.
  • MaybeUninit does nothing when it is dropped; this can lead to memory leaks.
  • If a panic happens in the middle of moving the values out (such as somewhere within the bar function), the array will be in a partially-uninitialized state. This is another (subtle) path where the MaybeUninit can be dropped, so now we have to know which values the array still owns and which have been moved out. We are responsible for freeing the values we still own and not the others.
  • Nothing prevents us from accidentally accessing the newly-invalidated values in the array ourselves.

正确的解决方案是跟踪数组中有多少值是有效/无效的.当数组被删除时,您可以删除剩余的有效项并忽略无效项.如果我们可以使这项工作适用于不同大小的数组,那就太好了……

The right solution is to track how many of the values in the array are valid / invalid. When the array is dropped, you can drop the remaining valid items and ignore the invalid ones. It'd also be really nice if we could make this work for arrays of different sizes...

这就是 arrayvec 的用武之地.它没有 exactem> 相同的实现(因为它更智能),但它确实具有相同的语义:

Which is where arrayvec comes in. It doesn't have the exact same implementation (because it's smarter), but it does have the same semantics:

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);
    }
}

这篇关于如何一次一个地将值移出数组?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆