Reputation: 1054
Rust offers borrow check at compile time. But if using Rc
and RefCell
, the check will be defered at runtime and a panic will be thrown when the program breaks the rule. Just like this:
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());
}
This code is still leaving the bug (maybe not a bug) to runtime and panicked. So should I avoid using Rc
and RefCell
as much as possible? And does this code count as safe code?
Upvotes: 5
Views: 2288
Reputation: 175
Since Rc
and RefCell
allow you to compile code that will potentially panic at runtime, they're not to be used lightly. You could use try_borrow_mut
instead of borrow_mut
to avoid the panic and handle the result yourself.
That being said, even if you prevent all panicking, Rc
and RefCell
have a cost at runtime since they keep a reference counter. In many cases you can avoid them by rewriting your code in a more rusty way.
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());
}
Is much simpler, and safe. Rust will take care of optimizations for you.
To answer your last question, using borrow_mut
is not considered as unsafe code since the code compiles even with the #![forbid(unsafe_code)]
directive
Upvotes: 6