我如何一次将值从一个数组中移出?

问题描述 投票:14回答:3

我拥有大小为3的数组的所有权,我想对其进行迭代,并在移动时将元素移出。基本上,我想为固定大小的数组实现IntoIterator

由于数组未在标准库中实现此特征(我理解为什么),是否有解决方法来获得所需的效果?我的对象不是Copy也不是Clone。我可以从数组中创建一个Vec,然后迭代到Vec中,但是我什至不知道该怎么做。

(有关信息,我想填写Complete的数组]

这里是一个简单的例子(尝试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); } }

送礼

playground
arrays rust move
3个回答
12
投票

    使用error[E0507]: cannot move out of borrowed content --> src/main.rs:14:13 | 14 | bar(*a); | ^^ cannot move out of borrowed content mem::replace将数组中的每个值替换为垃圾,取回原始值:
  1. mem::replace
  2. 使用mem::uninitialized将值保留在数组中,但又返回一个拥有的值:
  3. mem::uninitialized

只需在循环中几次执行其中之一,您就可以进行了。

只有一个小问题:您看到那些let one = unsafe { mem::replace(&mut v[0], mem::uninitialized()) };
bar(one);
吗?你猜到了;在更广泛的情况下,这是完全可怕的:

我们将使该数组充满任意位,仍然将其视为ptr::read。在

    this情况下,当数组超出范围时,不会发生任何特殊情况,因为类型ptr::read没有特殊的let two = unsafe { ptr::read(&v[0]) }; bar(two); 实现,但是如果存在,它将访问无效的内存。坏消息!
  • 可以使用unsafe忽略该数组并防止其被删除,但是...
  • 如果在将值移出中间(例如Foo函数中的某处)时发生了紧急情况,则该数组将处于部分未初始化的状态。这是可以调用Drop实现的另一条(微妙)路径,因此现在我们必须知道数组仍拥有哪些值以及哪些已移出。我们有责任释放我们仍然拥有的价值,而不是其他价值。
  • 没有什么可以防止我们意外地访问数组中新无效的值。
  • 正确的解决方案是
  • track数组中有多少个值有效/无效。删除数组时,可以删除剩余的有效项目,而忽略无效的项目。您还必须避免自动析构函数运行。如果我们可以对不同大小的数组进行这项工作,那也将非常好...

    Foo出现的位置。它没有exact相同的实现(因为它更聪明),但是它具有相同的语义:

    mem::forget

    在每晚的Rust中,您可以使用mem::forget获得按值数组迭代器:


    bar

  • 8
    投票
    std::array::IntoIter

    2
    投票
    非词法生存期功能(仅在

    nightly中使用)和定长切片模式,您可以移出数组:

    #![feature(array_value_iter)] use std::array::IntoIter; fn main() { let v: [Foo; 3] = [Foo, Foo, Foo]; for a in IntoIter::new(v) { bar(a); } } 但是,如果阵列很大,此解决方案将无法很好地扩展。
    另一种选择,如果您不介意额外的分配,则将数组装箱并将其转换为Option<Foo>

    Foo

    如果数组很大,那可能是个问题。但是,如果数组很大,则您不应该首先在堆栈中创建它!

    © www.soinside.com 2019 - 2024. All rights reserved.