一个 仙人掌堆栈或父指针树是一个堆栈,其中堆栈中的节点具有指向他们的父母,使堆积在多种方式爬升.
我试图在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)
恐怕你在这里失去了自己的只是想扔Option,Rc并RefCell在问题.
这些都不是万灵药,你需要了解什么时候才有意义,何时不需要.
以下是我得出的修订定义:
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:
Node唯一拥有它的价值,它永远不会分享它,因此这里没有意义Rc.Node可能是别名,但你仍然想要修改它的值,这需要将值包装在一个RefCell.Node 总是有一个父母,因为Cactus已经嵌入了一个无效的概念.并且Cactus:
Cactus可能是null,所以它是一个Option.Cactus与其他人共享其节点,因此Rc是必需的.Cactus永远不需要切换到另一个Node,它可以直接改变共享节点,所以RefCell是不必要的.从这里,我们可以实现Clone为Cactus(自动推导失败硬):
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)
请注意,我更改了一些签名:
parent返回Cactus,可以为null.我没有区别于null父项和null; 这是值得怀疑的,我只觉得有一个可能是空的Cactus包裹在一个Option奇怪的.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.
Run Code Online (Sandbox Code Playgroud)| 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()通过r4修复此问题即可.