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

标签 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之前。

最佳答案

您可能正在遭受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
        }
    }
}

另请参阅:

关于recursion - 在递归枚举上使用附带移动值错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28638757/

相关文章:

javascript - 使用按钮启动/停止 setTimeout 并防止点击次数越多计数速度越快

c - 如何创建对数字求和的递归函数?

dependencies - 如果相关功能被禁用,如何跳过依赖项

memory - 如何检查已编译类型的表示?

java - 使用递归绘制树

Java递归绘图

c# - 如何在 C# 中获取下一个(或上一个)枚举值

c++ - 使用具有相同名称的(无作用域的)基类枚举初始化派生类枚举

Java:枚举与整数

rust - 为什么元组或结构的大小不是成员的总和?