我试图实现简单的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 字段,而不是它的副本。
我只看到两种选择:
RcString(Rc<RefCell<String>>)
代替原始String
。但它会产生大量的样板代码和一些开销。is_text_equal(&self, text: &String)
。但它很难看。此外,我有可能做出完全错误的事情。
答案 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
的内部值就被认为是不可改变的。在这种情况下,text
是Ref
对象,因此借用内部值。在set_text
中,你试图可变地借用内在价值。
当然你也可以自己实现一些实用功能,正如你已经说过的那样(2。)。
老实说,我会说你应该重新考虑你的设计。真的需要RefCell
吗?通常情况并非如此。通常RefCells
仅用于私有隐藏变量,并且永远不会向用户公开。上述问题是不这样做的一个原因。
当然这个答案并不令人满意,但我认为没有更好的解决方案。
&String
引用并不比&str
引用更强大。因此通常只使用&str
。 &Vec<T>
(几乎不使用)和&[T]
也是如此。请注意,这仅对不可变引用有效 - &mut String
比&mut str
强大得多。