遍历列表时,借用的RefCell的持续时间不够长 [英] Borrowed RefCell does not last long enough when iterating over a list

查看:63
本文介绍了遍历列表时,借用的RefCell的持续时间不够长的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试实现一个链接列表,以了解Rust中的智能指针.我定义了一个 Node :

I'm trying to implement a linked list to understand smart pointers in Rust. I defined a Node:

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

struct Node {
    val: i32,
    next: Option<Rc<RefCell<Node>>>,
}

并像

fn iterate(node: Option<&Rc<RefCell<Node>>>) -> Vec<i32> {
    let mut p = node;
    let mut result = vec![];

    loop {
        if p.is_none() {
            break;
        }

        result.push(p.as_ref().unwrap().borrow().val);

        p = p.as_ref().unwrap().borrow().next.as_ref();
    }

    result
}

编译器报告错误:

error[E0716]: temporary value dropped while borrowed
  --> src/main.rs:27:13
   |
27 |         p = p.as_ref().unwrap().borrow().next.as_ref();
   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^              -
   |             |                                         |
   |             |                                         temporary value is freed at the end of this statement
   |             |                                         ... and the borrow might be used here, when that temporary is dropped and runs the destructor for type `std::cell::Ref<'_, Node>`
   |             creates a temporary which is freed while still in use
   |             a temporary with access to the borrow is created here ...
   |
   = note: consider using a `let` binding to create a longer lived value

发生了什么事?我们不能使用引用来迭代这样定义的节点吗?

What happened? Can't we use a reference to iterate on a node defined this way?

推荐答案

您需要克隆 Rc :

use std::cell::RefCell;
use std::rc::Rc;

struct Node {
    val: i32,
    next: Option<Rc<RefCell<Node>>>,
}

fn iterate(node: Option<Rc<RefCell<Node>>>) -> Vec<i32> {
    let mut p = node;
    let mut result = vec![];

    loop {
        let node = match p {
            None => break,
            Some(ref n) => Rc::clone(n), // Clone the Rc
        };

        result.push(node.as_ref().borrow().val); //works because val is Copy
        p = match node.borrow().next {
            None => None,
            Some(ref next) => Some(Rc::clone(next)), //clone the Rc
        };
    }

    result
}

fn main() {
    let node = Some(Rc::new(RefCell::new(Node {
        val: 0,
        next: Some(Rc::new(RefCell::new(Node { val: 1, next: None }))),
    })));

    let result = iterate(node);
    print!("{:?}", result)
}

这是必要的,因为您试图在需要更长寿命的上下文中使用寿命较短的变量.循环迭代后,将删除 p.as_ref().unwrap().borrow()的结果,但您尝试在下一个循环中使用其成员(这称为"免费使用后",Rust的设计目标之一就是防止这种情况发生.)

This is necessary because you are trying to use a variable with a shorter lifespan in a context that requires a longer lifespan. The result of p.as_ref().unwrap().borrow() is dropped (i.e. freed, de-allocated) after the loop iteration, but you are trying to use its members in the next loop (this is called use after free and one of the design goals of Rust is to prevent that).

问题在于借用不拥有对象.如果您想在下一个循环中使用 next 作为 p,那么 p 将必须拥有该对象.这可以通过 Rc (即引用计数")实现,并允许单个线程中有多个所有者.

The issue is that borrows do not own the object. If you want to use the next as p in the next loop, then p will have to own the object. This can be achieved with Rc (i.e. 'reference counted') and allows for multiple owners in a single thread.

如果 Node :: next 的定义是 Option< Box< RefCell< Node>>> ,该如何遍历此列表呢?

What if the definition of Node::next is Option<Box<RefCell<Node>>>, how to iterate over this list?

是的,我也对 RefCell 感到非常困惑,如果没有 RefCell ,我们只能使用引用来遍历列表,但是如果使用 RefCell .我什至尝试添加 Ref 的向量来保存引用,但仍然无法成功.

Yes, I'm also very confused with RefCell, without RefCell we can iterate over list using reference only, but will fail with RefCell. I even tried to add a vector of Ref to save the reference, but still can not success.

如果您放下 RefCell ,则可以这样进行迭代:

If you drop the RefCell you can iterate it like this:

struct Node {
    val: i32,
    next: Option<Box<Node>>,
}

fn iterate(node: Option<Box<Node>>) -> Vec<i32> {
    let mut result = vec![];
    let mut next = node.as_ref().map(|n| &**n);

    while let Some(n) = next.take() {
        result.push(n.val);

        let x = n.next.as_ref().map(|n| &**n);
        next = x;
    }

    result
}

fn main() {
    let node = Some(Box::new(Node {
        val: 0,
        next: Some(Box::new(Node { val: 1, next: None })),
    }));

    let result = iterate(node);
    print!("{:?}", result)
}

也许也可以使用 RefCell ,但是我无法解决生命周期问题.

Maybe it's possible with a RefCell as well, but I was not able to work around the lifetime issues.

这篇关于遍历列表时,借用的RefCell的持续时间不够长的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆