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

时间:2015-02-20 21:44:43

标签: recursion enums rust

我有一个递归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(..)项而不是Option<Box<Whatever>>之类的嵌套项目可以应用两阶段技巧。我试过的例子:

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

1 个答案:

答案 0 :(得分:2)

你可能患有issue 16223(另见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中拉出来,操纵 ,然后将操纵值重新放回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
        }
    }
}

另见: