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

时间:2018-01-17 04:58:34

标签: tree rust stack

A Cactus Stack or Parent Pointer Tree是一个堆栈,其中堆栈中的节点具有指向其父节点的指针,因此堆栈可以多种方式爬行。

我尝试使用Rc<RefCell<T>>模式在this immutable implementation之外的Rust中实现一个可变的Cactus Stack来传递共享内存:

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);
    }    
}

playground

我的问题是从节点获取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

1 个答案:

答案 0 :(得分:5)

我担心你只是试图在这个问题上抛出git logOptionRc而迷失在这里。

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

以下是我到达的修订定义:

RefCell

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

让我们分析pub struct Cactus<T>(Option<Rc<Node<T>>>); struct Node<T> { value: RefCell<T>, parent: Cactus<T>, len: usize, }

  1. Node唯一拥有它的值,从不共享它,因此Node没有任何意义。
  2. Rc可能有别名,但您仍想修改其值,这需要将值包装在Node中。
  3. RefCell 总是有父母,因为Node已经嵌入了无效的概念。
  4. Cactus

    1. Cactus可能为null,因此它是Cactus
    2. Option与其他人共享其节点,因此需要Cactus
    3. Rc永远不需要切换到另一个Cactus,它可以直接改变共享节点,因此不需要Node
    4. 从那里,我们可以为RefCellthe automatic derivation fails hard)实施Clone

      Cactus

      注意使用impl<T> Clone for Cactus<T> { fn clone(&self) -> Self { Cactus(self.0.clone()) } } 来获取lambda中的as_ref;如果没有它,&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)
          }
      }
      

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

      1. parent返回Cactus,可能为null。我没有区别于null父项和null;这是值得怀疑的,我只觉得在Cactus中包含一个可能为空的Option是奇怪的。
      2. value返回对RefCell的引用(包含在Option中),以便调用者可以调用borrow_mut并改变实际值。
      3. 这需要对测试进行一些调整:

        #[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);
        }
        

        大多数情况下,正如您所见,在.borrow()之后调用.unwrap()

        值得注意的是,最新的一行无法编译:{{1​​}}返回一个临时值,我们试图从中获取一个引用;编译器抱怨在删除临时值之后使用此引用,可能详细说明了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 替换为r6.parent()即可解决此问题。