在递归枚举上使用附带移动的值错误

Use of collaterally moved value error on a recursive enum

我有一个用于实现列表的递归 Item 结构:

#[derive(Debug)]
pub enum Item<T> {
    Cons(T, Box<Item<T>>),
    Nil,
}

在实现一个元素接一个元素插入的函数时,我发现 Rust 编译器对我的代码不太满意:

pub fn add_after<T>(it: Box<Item<T>>, val: T) -> Box<Item<T>> {
    match *it {
        Item::Nil => return it,
        Item::Cons(a, b) => {
            let itm = Box::new(Item::Cons(val, b));
            return Box::new(Item::Cons(a, itm));
        }
    }
}

我得到的错误对于新手来说是相当模糊的:

error[E0382]: use of collaterally moved value: `(it as Item::Cons).1`
  --> src/main.rs:12:23
   |
12 |         Item::Cons(a, b) => {
   |                    -  ^ value used here after move
   |                    |
   |                    value moved here
   |
   = note: move occurs because the value has type `T`, which does not implement the `Copy` trait

Another similar question 建议分两步进行解包阶段,但不能在这里使用,因为我们需要直接解包一个双字段 Cons(..) 项目而不是像 [=17= 这样的嵌套项目] 可以应用两阶段技巧的地方。我尝试过的例子:

pub fn add_after<T>(it: Box<Item<T>>, val: T) -> Box<Item<T>> {
    match *it {
        Item::Nil => return it,
        Item::Cons(..) => {
            let Item::Cons(a, b) = *it;
            let itm = Box::new(Item::Cons(val, b));
            return Box::new(Item::Cons(a, itm));
        }
    }
}

但是我得到另一个错误:

error[E0005]: refutable pattern in local binding: `Nil` not covered
  --> src/main.rs:13:17
   |
13 |             let Item::Cons(a, b) = *it;
   |                 ^^^^^^^^^^^^^^^^ pattern `Nil` not covered

虽然我很确定这在这一点上是详尽无遗的,因为我们之前匹配了一个Cons

您可能正在遭受 issue 16223 (see also 22205 的困扰,它具有更接近的再现),尽管今天的非词汇生命周期并没有解决这个问题。这似乎排除了通过 Box 解构多个事物。

这是一种解决它的方法,虽然它不是有效的方法,因为它会不必要地解除分配和重新分配:

#[derive(Debug)]
pub enum Item<T> {
    Cons(T, Box<Item<T>>),
    Nil,
}

pub fn add_after<T>(it: Box<Item<T>>, val: T) -> Box<Item<T>> {
    match { *it } {
        Item::Nil => Box::new(Item::Nil),
        Item::Cons(a, b) => {
            let itm = Box::new(Item::Cons(val, b));
            Box::new(Item::Cons(a, itm))
        }
    }
}

fn main() {}

更详细的方法是从 Box 中提取值,操纵 that,然后将操纵的值放回 Box。这应该减少分配量:

use std::mem;

pub fn add_after<T>(mut item: Box<Item<T>>, val: T) -> Box<Item<T>> {
    let unboxed_value = mem::replace(&mut *item, Item::Nil);

    match unboxed_value {
        Item::Nil => item,
        Item::Cons(a, b) => {
            let itm = Box::new(Item::Cons(val, b));
            *item = Item::Cons(a, itm);
            item
        }
    }
}

另请参阅:

  • Collaterally moved error when deconstructing a Box of pairs