【问题标题】:How can I iteratively traverse a tree built with Rc<RefCell<T>> while mutably matching on enums?如何迭代遍历用 Rc<RefCell<T>> 构建的树,同时在枚举上进行可变匹配?
【发布时间】:2021-12-21 17:40:17
【问题描述】:

在借用检查器的指导下,如果没有递归,我找不到进行简单树遍历的方法。

Advent of Code 2021 Day 18 的数字可以用一对数字或一个值来表示(非常适合 Rust 枚举)。还有一些问题是我使用堆栈来遍历树,所以我使用Rc&lt;RefCell&lt;T&gt;&gt; 来表示子节点。这使我得到以下表示:

use std::{cell::RefCell, rc::Rc};

enum Number {
    Pair(Rc<RefCell<Number>>, Rc<RefCell<Number>>),
    Value(u8),
}

我正在尝试创建一个函数来更新Number 中最左边的值。编写递归版本进行了几次尝试,但最终成功了。但是,无论我尝试什么,我都无法获得一个迭代版本来编译。匹配节点和必须借用RefMut 的组合导致我遇到借用/生命周期问题。这就是我想要做的:

fn propagate_left(mut node: &Rc<RefCell<Number>>, val: u8) {
    loop {
        let mut ref_mut = node.borrow_mut();
        match &mut (*ref_mut) {
            Number::Pair(left_node, _) => {
                node = left_node;
            }
            Number::Value(number_val) => {
                *number_val += val;
                return;
            }
        }
    }
}
error[E0597]: `ref_mut` does not live long enough
  --> src/lib.rs:11:22
   |
8  | fn propagate_left(mut node: &Rc<RefCell<Number>>, val: u8) {
   |                             - let's call the lifetime of this reference `'1`
...
11 |         match &mut (*ref_mut) {
   |                      ^^^^^^^ borrowed value does not live long enough
12 |             Number::Pair(left_node, _) => {
   |                          --------- assignment requires that `ref_mut` is borrowed for `'1`
...
20 |     }
   |     - `ref_mut` dropped here while still borrowed

有什么方法可以在不使用不安全代码或递归的情况下完成这项工作?

我在结构中使用Vec 来存储和索引,以避免在其他谜题中出现此类问题,但我想尝试在这个谜题中使用引用。

【问题讨论】:

  • 我理解,但做这些谜题的目的是在 Rust 中尝试不同的东西。在这里,我想在没有递归的情况下进行遍历,使用使使用 Box 不切实际的堆栈。我确信还有其他(更简单)的方法可以做到这一点,我只是想知道是否有解决这个特定问题的方法而不改变前提。
  • 正如我提到的,我在拼图的另一部分使用堆栈,如果我没记错的话,我在遍历时无法复制 Box 指针以将其存储在我的堆栈中。
  • 在谜题的另一部分使用堆栈——我们无法帮助处理不属于问题的代码。如果您有需要尊重的限制,则需要将它们包含在此处,作为代码而不是散文。否则你会得到不可接受的答案。
  • 是的,谢谢您的回答!我只是想知道我是否错过了一种在不更改数据表示或使用递归的情况下编写此代码的明显方法(我知道这些解决了问题)。抱歉,这似乎是一个简单的语法问题。如果您将代码添加为答案,我可以接受

标签: rust


【解决方案1】:

您尝试的问题是node.borrow_mut() 的返回值的生命周期与node 相关联,但您试图将该结果存储到具有调用者提供的生命周期的变量中。当当前迭代的borrow_mut() 结束时,引用(由left_node 使用)过期,您不能将生命周期较短的内容存储到生命周期较长的变量中——这就是内存安全的全部要点。

解决方法是避免同时使用引用 (&amp;) 和引用计数 (Rc)。仅使用Rc,您可以克隆孩子并替换临时变量:

use std::{cell::RefCell, rc::Rc};

enum Number {
    Pair(Rc<RefCell<Number>>, Rc<RefCell<Number>>),
    Value(u8),
}

fn propagate_left(mut node: Rc<RefCell<Number>>, val: u8) {
    loop {
        let next = match &mut *node.borrow_mut() {
            Number::Pair(left_node, _) => Rc::clone(left_node),
            Number::Value(number_val) => {
                *number_val += val;
                return;
            }
        };

        node = next;
    }
}

您也可以只使用引用并完全避免Rc / RefCell

enum Number {
    Pair(Box<Number>, Box<Number>),
    Value(u8),
}

fn propagate_left(mut node: &mut Number, val: u8) {
    loop {
        match node {
            Number::Pair(left_node, _) => {
                node = left_node;
            }
            Number::Value(number_val) => {
                *number_val += val;
                return;
            }
        };
    }
}

另见:

【讨论】:

    猜你喜欢
    • 2020-01-15
    • 1970-01-01
    • 2021-05-22
    • 1970-01-01
    • 1970-01-01
    • 2019-12-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多