获取Rc背后的对象

时间:2016-06-29 16:46:12

标签: rust

我试图实现简单的DOM结构,但是对生锈(或不是)内存模型感到困惑。

这是一个简单的例子:

struct NodeData {
    text: String,
}

struct Node(Rc<RefCell<NodeData>>);

impl Node {
    fn new() -> Node {
        Node(Rc::new(RefCell::new(NodeData {
            text: String::new()
        })))
    }

    fn set_text(&self, text: String) {
        self.0.borrow_mut().text = text;
    }

    // it works, but has unneeded, expensive clone
    // pub fn text(&self) -> String {
    //     self.0.borrow().text.clone()
    // }

    pub fn text(&self) -> &String {
        // ???
    }
}

fn main() {
    let node = Node::new();
    node.set_text("text".to_string());
    if node.text() == "text" {
        println!("equal");
    }
}

我不明白如何引用 text 字段,而不是它的副本。

我只看到两种选择:

  1. 使用RcString(Rc<RefCell<String>>)代替原始String。但它会产生大量的样板代码和一些开销。
  2. 实施方法,如is_text_equal(&self, text: &String)。但它很难看。
  3. 此外,我有可能做出完全错误的事情。

1 个答案:

答案 0 :(得分:3)

解释

这里的问题是您在NodeData内管理某些内容(RefCell)。为了使RefCell起作用,它不能无限制地给出引用。相反,获取内部数据引用的唯一方法是使用范围保护。来自RefCell documentation

fn borrow(&self) -> Ref<T>

而不是像您期望的那样返回&T,而是返回Ref。这个Ref对象反过来可以通过deref - 运算符方法为您提供参考。但只要Ref对象存在,返回的引用才有效!这样RefCell可以跟踪所有借款。

可能的解决方案

您可以返回Ref对象,用户几乎可以将其用作简单的&String。然而,这很容易导致恐慌,因为用户不小心再次借用它。看看这个:

let text = my_node.text();
println!("the text is {} chars long", text.len());
my_node.set_text("hi".into());   // boom!

问题是只要RefCell对象存活,Ref的内部值就被认为是不可改变的。在这种情况下,textRef对象,因此借用内部值。在set_text中,你试图可变地借用内在价值。

当然你也可以自己实现一些实用功能,正如你已经说过的那样(2。)。

老实说,我会说你应该重新考虑你的设计。真的需要RefCell吗?通常情况并非如此。通常RefCells仅用于私有隐藏变量,并且永远不会向用户公开。上述问题是不这样做的一个原因。

当然这个答案并不令人满意,但我认为没有更好的解决方案。

其他提示

  • &String引用并不比&str引用更强大。因此通常只使用&str&Vec<T>(几乎不使用)和&[T]也是如此。请注意,这仅对不可变引用有效 - &mut String&mut str强大得多。