如何在Rust中实现可变的仙人掌堆栈?

Jac*_*cob 2 tree stack rust

一个 仙人掌堆栈或父指针树是一个堆栈,其中堆栈中的节点具有指向他们的父母,使堆积在多种方式爬升.

我试图在Rust中实现一个可变的Cactus Stack,基于这个不可变的实现,使用Rc<RefCell<T>>模式来传递共享内存:

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

#[derive(Clone, Default)]
pub struct Cactus<T> {
    node: Option<Rc<RefCell<Node<T>>>>,
}

#[derive(Clone)]
pub struct Node<T> {
    val: Rc<RefCell<T>>,
    parent: Option<Rc<RefCell<Node<T>>>>,
    len: usize,
}

impl<T> Cactus<T> {
    pub fn new() -> Cactus<T> {
        Cactus { node: None }
    }

    pub fn is_empty(&self) -> bool {
        self.node.is_none()
    }

    pub fn len(&self) -> usize {
        self.node.as_ref().map_or(0, |x| x.borrow().len)
    }

    pub fn child(&self, val: T) -> Cactus<T> {
        Cactus {
            node: Some(Rc::new(RefCell::new(Node {
                val: Rc::new(RefCell::new(val)),
                parent: self.node.clone(),
                len: self.node.as_ref().map_or(1, |x| x.borrow().len + 1),
            }))),
        }
    }

    pub fn parent(&self) -> Option<Cactus<T>> {
        self.node.as_ref().map(|n| Cactus {
            node: n.borrow().parent.clone(),
        })
    }

    pub fn val(&mut self) -> Option<Rc<RefCell<T>>> {
        self.node.map(|n| n.borrow_mut().val.clone())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_simple() {
        let r = Cactus::new();
        assert!(r.is_empty());
        assert_eq!(r.len(), 0);
        assert!(r.val().is_none());
        assert!(r.parent().is_none());
        let r2 = r.child(2);
        assert!(!r2.is_empty());
        assert_eq!(r2.len(), 1);
        assert_eq!(*r2.val().unwrap(), 2);
        let r3 = r2.parent().unwrap();
        assert_eq!(r3.is_empty(), true);
        assert_eq!(r3.len(), 0);
        let r4 = r.child(3);
        assert_eq!(r4.len(), 1);
        assert_eq!(*r4.val().unwrap(), 3);
        let r5 = r4.parent().unwrap();
        assert!(r5.is_empty());
        let r6 = r4.child(4);
        assert_eq!(r6.len(), 2);
        assert_eq!(*r6.val().unwrap(), 4);
        assert_eq!(*r6.parent().unwrap().val().unwrap(), 3);
    }    
}
Run Code Online (Sandbox Code Playgroud)

操场

我的问题是val从节点获取:

error[E0308]: mismatched types
  --> src/main.rs:64:9
   |
64 |         assert_eq!(*r2.val().unwrap(), 2);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::cell::RefCell`, found integral variable
   |
   = note: expected type `std::cell::RefCell<{integer}>`
              found type `{integer}`
   = note: this error originates in a macro outside of the current crate

error[E0308]: mismatched types
  --> src/main.rs:70:9
   |
70 |         assert_eq!(*r4.val().unwrap(), 3);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::cell::RefCell`, found integral variable
   |
   = note: expected type `std::cell::RefCell<{integer}>`
              found type `{integer}`
   = note: this error originates in a macro outside of the current crate

error[E0308]: mismatched types
  --> src/main.rs:75:9
   |
75 |         assert_eq!(*r6.val().unwrap(), 4);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::cell::RefCell`, found integral variable
   |
   = note: expected type `std::cell::RefCell<{integer}>`
              found type `{integer}`
   = note: this error originates in a macro outside of the current crate

error[E0308]: mismatched types
  --> src/main.rs:76:9
   |
76 |         assert_eq!(*r6.parent().unwrap().val().unwrap(), 3);
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::cell::RefCell`, found integral variable
   |
   = note: expected type `std::cell::RefCell<{integer}>`
              found type `{integer}`
   = note: this error originates in a macro outside of the current crate
Run Code Online (Sandbox Code Playgroud)

Mat*_* M. 5

恐怕你在这里失去了自己的只是想扔Option,RcRefCell在问题.

这些都不是万灵药,你需要了解什么时候才有意义,何时不需要.

以下是我得出的修订定义:

pub struct Cactus<T>(Option<Rc<Node<T>>>);

struct Node<T> {
    value: RefCell<T>,
    parent: Cactus<T>,
    len: usize,
}
Run Code Online (Sandbox Code Playgroud)

免责声明:我试图推断出你实际需要可变性的地方,以及你不需要的地方,因为你从未真正解释过它.我的推断可能在某些地方不正确; 例如,我决定切换父母是不必要的.

我们来分析一下Node:

  1. Node唯一拥有它的价值,它永远不会分享它,因此这里没有意义Rc.
  2. Node可能是别名,但你仍然想要修改它的值,这需要将值包装在一个RefCell.
  3. Node 总是有一个父母,因为Cactus已经嵌入了一个无效的概念.

并且Cactus:

  1. Cactus可能是null,所以它是一个Option.
  2. Cactus与其他人共享其节点,因此Rc是必需的.
  3. Cactus永远不需要切换到另一个Node,它可以直接改变共享节点,所以RefCell是不必要的.

从这里,我们可以实现CloneCactus(自动推导失败硬):

impl<T> Clone for Cactus<T> {
    fn clone(&self) -> Self { Cactus(self.0.clone()) }
}
Run Code Online (Sandbox Code Playgroud)

注意使用在lambda中as_ref获得a &Rc; 没有它,map_or呼叫将试图Rc移出self.0其中被禁止,因为self借用.

其他功能自然如下:

impl<T> Cactus<T> {
    pub fn new() -> Cactus<T> { Cactus(None) }

    pub fn is_empty(&self) -> bool { self.0.is_none() }

    pub fn len(&self) -> usize { self.0.as_ref().map_or(0, |n| n.len) }

    pub fn child(&self, val: T) -> Cactus<T> {
        let node = Node {
            value: RefCell::new(val),
            parent: self.clone(),
            len: self.len() + 1,
        };
        Cactus(Some(Rc::new(node)))
    }

    pub fn parent(&self) -> Cactus<T> {
        self.0.as_ref().map_or(Cactus(None), |n| n.parent.clone())
    }

    pub fn value(&self) -> Option<&RefCell<T>> {
        self.0.as_ref().map(|n| &n.value)
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,我更改了一些签名:

  1. parent返回Cactus,可以为null.我没有区别于null父项和null; 这是值得怀疑的,我只觉得有一个可能是空的Cactus包裹在一个Option奇怪的.
  2. value返回对RefCell(wrapped in Option)的引用,以便调用者可以调用borrow_mut并改变实际值.

这需要对测试进行一些调整:

#[test]
fn test_simple() {
    let r = Cactus::new();
    assert!(r.is_empty());
    assert_eq!(r.len(), 0);
    assert!(r.value().is_none());
    assert!(r.parent().is_empty());

    let r2 = r.child(2);
    assert!(!r2.is_empty());
    assert_eq!(r2.len(), 1);
    assert_eq!(*r2.value().unwrap().borrow(), 2);

    let r3 = r2.parent();
    assert_eq!(r3.is_empty(), true);
    assert_eq!(r3.len(), 0);

    let r4 = r.child(3);
    assert_eq!(r4.len(), 1);
    assert_eq!(*r4.value().unwrap().borrow(), 3);

    let r5 = r4.parent();
    assert!(r5.is_empty());

    let r6 = r4.child(4);
    assert_eq!(r6.len(), 2);
    assert_eq!(*r6.value().unwrap().borrow(), 4);
    assert_eq!(*r6.parent().value().unwrap().borrow(), 3);
}
Run Code Online (Sandbox Code Playgroud)

大多数情况下,正如你所看到的,呼唤.borrow()之后.unwrap().

值得注意的是,最新的一行无法编译:r6.parent()返回一个临时值,我们试图从中获取一个引用; 编译器抱怨在删除临时值之后使用此引用,可能作为实现方式的详细信息assert_eq.

   |
74 |         assert_eq!(*r6.parent().value().unwrap().borrow(), 3);
   |         ^^^^^^^^^^^^-----------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |         |           |
   |         |           temporary value created here
   |         temporary value dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created
   = note: consider using a `let` binding to increase its lifetime
   = note: this error originates in a macro outside of the current crate
Run Code Online (Sandbox Code Playgroud)

只需r6.parent()通过r4修复此问题即可.