Smart Pointers in Rust (Box, Rc, Arc, RefCell, Mutex, etc…)

Emir Buğra KÖKSALAN tarafından tarihinde yayınlandı

As you know Rust is null-safe language and no garbage collection. It has some pointers which have some rules for preventing shooting our foot. We can’t use pointers directly as we wish in Rust, we must use them behind of some types, these types have special name: Smart Pointer. Actually there is no “smart”, they have only some strict rules, that’s all. Let us deep dive into the some basic topics like pointer, reference, immutability etc…

What is pointer?

Pointer is basically a variable which have memory location, not value itself. Value can live in stack or heap but pointers must live in stack. Pointers have specific length and because of that compiler puts pointers to stack itself. But as I said before, pointers can point to stack or heap. We need to remember that, pointers have two behaviours: reach to value, create pointer from value. In Rust the first one name is dereference, second one name is reference.

What is smart pointer?

As I said before, there isn’t anything which has an intelligence or there isn’t anything which we can call ‘smart’, only there are some pointer management struct which have some rules about managing access ways to a variable. We can’t use pointers as we wish in Rust and because of that rust must provide some mechanisms for securely reaching to a variable. With these mechanisms we can securely use threads and we can trust to our app for it doesn’t have any memory leak or memory related bug.

How smart pointers handle these rules?

In Rust we can use unsafe code. This sounds like not safe but actually unsafe code means that some strict rules not applying in this area, that’s all. For example we can use multiple mutable reference, dereferencing a raw pointer etc. In unsafe area we can make safe things with additional checks which we can’t do them in safe area. Smart pointers actually makes this.

And one last thing, you need to open your mind to new names. Mostly Rust developers have strong background knowledge of another languages, which have garbage collection or unsecured pointer usage (Java, C#, PHP, Javascript, C, C++ etc). Rust has some different rules which not exist all of these languages, because of that there are different things with different names which you never heard before.

Rc (Reference Counted)

Counting referencing is only helper thing to another smart pointers like RefCell, Mutex etc. We will discuss all of these but you must only keep that thing in your mind, Rc saves any variable inside of it and counts the references. And we can’t mutate the variable. Rc only gives us references, that’s all. Because of that we pass another smart pointer to inside of Rc.

Some rules about Rc:

  • Rc implements the Deref trait but you can’t use * character for moving out the inner data. You can only access immutable reference to inner value.
    • let mut rc_string = Rc::new("test".to_string());
      let inner_val: String = *rc_string; // cannot move out of an Rc
      let inner_val: &String = &*rc_string;
      
  • You can use * for reaching the wrapped value but you can’t modify it. Because of that we encourage you to use Rc with another smart pointers which described below

RefCell

Basically RefCell‘s purpose is only applying borrow check rules at runtime. We can get unlimited reference and single mutable reference to inside value and all of these checks made by RefCell, not Rust’s compiler. Why this thing exist in Rust? Because RefCell isn’t intended to be used alone, we must use it with Rc. Let’s go to code:

let rcell_val_1 = RefCell::new(10);
let mut rcell_mut_val_1 = rcell_val_1.borrow_mut();

// This is valid for compiler but it generates error at runtime.
// let mut rcell_mut_val_2 = rcell_val_1.borrow_mut();
*rcell_mut_val_1 = 20;
println!("Val: {}", rcell_val_1.borrow()); // 20

Now let’s combine Rc with RefCell and get/set the value:

let rc2 = Rc::new(RefCell::new(10));
println!("{:?}", rc2.borrow()); // 10

// `*rc2.borrow_mut()` section is exactly points to our value (10)
// and we can assign new value to there.
*rc2.borrow_mut() = 20;
println!("{:?}", rc2.borrow()); // 20

// Also the replace() function makes same thing
rc2.replace(30);
println!("{:?}", rc2.borrow()); // 30

// ⏯️ play

Smart pointers provide us everything we need while adhering to the Rust rules and they are awesome.

Kategoriler: Rust

Emir Buğra KÖKSALAN

Java & PHP Developer

0 yorum

Bir yanıt yazın

Avatar placeholder

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

Time limit is exhausted. Please reload the CAPTCHA.

Bu site, istenmeyenleri azaltmak için Akismet kullanıyor. Yorum verilerinizin nasıl işlendiği hakkında daha fazla bilgi edinin.