在Rust中,我是否应该尽可能避免使用Rc和RefCell?

4

Rust提供了在编译时的借用检查。但如果使用RcRefCell,则检查将被推迟到运行时,并且当程序违反规则时会引发异常,就像这样:

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

fn func1(reference: Rc<RefCell<String>>){
    let mut a = reference.borrow_mut();
    *a = String::from("func1");
    func2(reference.clone());
}

fn func2(reference: Rc<RefCell<String>>){
    let mut a = reference.borrow_mut();
    *a = String::from("func2");
    func3(reference.clone());
}

fn func3(reference: Rc<RefCell<String>>){
    let mut a = reference.borrow_mut();
    *a = String::from("func3");
}


fn main() {
    let a = Rc::new(RefCell::new(String::from("hello")));
    func1(a.clone());
}

这段代码仍然将缺陷(也许不是真正的缺陷)留给了运行时并且会导致程序崩溃。所以我应该尽量避免使用 RcRefCell 吗?这段代码算作安全代码吗?


取决于您的具体情况。有些事情仅凭参考和编译时借用并不容易实现。 - Alexey Larionov
2
指导方针是尽可能采用Rustacean编译时方式,如果太难,则寻找运行时借用。 - Alexey Larionov
1个回答

5

由于 RcRefCell 允许您编写在运行时可能会出现错误的代码,因此不应轻易使用它们。您可以使用 try_borrow_mut 代替 borrow_mut 来避免 panic 并自行处理结果。

话虽如此,即使您防止了所有的 panic,RcRefCell 在运行时仍有成本,因为它们保留了引用计数器。在许多情况下,您可以通过以更加 "rusty" 的方式重写代码来避免使用它们。

fn func1(mut string: String) -> String {
    string = "func1".into();
    func2(string)
}

fn func2(mut string: String) -> String {
    string = "func2".into();
    func3(string)
}

fn func3(string: String) -> String {
    "func3".into()
}

fn main() {
    let a = func1("hello".into());
}

这很简单,也很安全。Rust会为你处理优化。

回答你的最后一个问题,使用borrow_mut不被视为不安全的代码,因为即使使用#![forbid(unsafe_code)]指令编译代码也可以通过。


传递字符串会不会比增加引用计数对性能更糟糕? - Alen Siljak
1
不,字符串是通过引用传递的,所以在移动它们时很便宜。 - Billbucket
这篇文章过时了吗?https://blog.logrocket.com/understanding-rust-string-str,它提到:"如果您传递拥有的String实例而不是可变借用(&mut String)或只读视图(&str),将触发更多的分配并且需要保留更多的内存。" - Alen Siljak
在这个特定的情况下,字符串的所有权转移,字符串被移动。但是标题中的问题更加普遍,而且关于字符串的话题在内存成本方面尤为有趣。 - Alen Siljak

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接