Struggle with Rust compiler — 10 Link to heading

Let’s talk variant matching of an enum behind Rc<RefCell<T>>. Consider the following:

Struggle with Rust compiler — type match ref Link to heading

Let’s talk about variant matching of an enum behind Rc<RefCell<T>>. Consider the following:

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

enum Dessert{
  Donuts,
  Candies,
}

fn eat(dessert: Rc<RefCell<Dessert>>) {
    match dessert.borrow() {
        Dessert::Donuts => { println!("donuts!"); }
        Dessert::Candies => { println!("candies!"); }
    }
}

Well, will the code compile? Trying this on Rust Playground, it doesn’t:

error[E0308]: mismatched types
  --> src/lib.rs:11:9
   |
5  |   Donuts,
   |   ------ unit variant defined here
...
10 |     match dessert.borrow() {
   |           ---------------- this expression has type `Ref<'_, Dessert>`
11 |         Dessert::Donuts => { println!("donuts!"); }
   |         ^^^^^^^^^^^^^^^ expected `Ref<'_, Dessert>`, found `Dessert`
   |
   = note: expected struct `Ref<'_, Dessert, >`
                found enum `Dessert`

error[E0308]: mismatched types
  --> src/lib.rs:12:9
   |
6  |   Candies,
   |   ------- unit variant defined here
...
10 |     match dessert.borrow() {
   |           ---------------- this expression has type `Ref<'_, Dessert>`
11 |         Dessert::Donuts => { println!("donuts!"); }
12 |         Dessert::Candies => { println!("candies!"); }
   |         ^^^^^^^^^^^^^^^^ expected `Ref<'_, Dessert>`, found `Dessert`
   |
   = note: expected struct `Ref<'_, Dessert, >`
                found enum `Dessert`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` (lib) due to 2 previous errors

Apparently, dessert.borrow() yields Ref<'_, Dessert>, which can’t match with Dessert variants. One solution is to add * to dereference it

-    match dessert.borrow() {
+    match *dessert.borrow() {

This code now compiles and works well.

Let’s make it a bit more interesting by letting each variant a hold variable and mutate it

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

enum Dessert{
  Donuts(i32),
  Candies(i32),
}

fn eat(dessert: Rc<RefCell<Dessert>>) {
    match *dessert.borrow_mut() {
        Dessert::Donuts(mut n) => { n -= 1; println!("{n} donuts left!"); }
        Dessert::Candies(mut n) => { n -= 1; println!("{n} candies left!"); }
    }
}

fn main() {
    let candy = Rc::new(RefCell::new(Dessert::Candies(10)));
    eat(Rc::clone(&candy));
    eat(Rc::clone(&candy));
    eat(Rc::clone(&candy));
}

What do you think the program will output? Let’s try it on Rust Playground:

9 candies left!
9 candies left!
9 candies left!

The candies are not being decremented because *dessert.borrow_mut() produce a copy of n ! We are modifying the copy, not the original value itself. Then how do we fix it? Maybe we need to reference it back

-    match *dessert.borrow_mut() {
+    match &*dessert.borrow_mut() {

With this change, here is the output from Rust Playground:

9 candies left!
9 candies left!
9 candies left!

Unfortunately, it didn’t fix the issue. To fix this, one needs to add ref into the match type to force reference, and use *n to modify n as shown below

-        Dessert::Donuts(mut n) => { n -= 1; println!("{n} donuts left!"); }
-        Dessert::Candies(mut n) => { n -= 1; println!("{n} candies left!"); }
+        Dessert::Donuts(ref mut n) => { *n -= 1; println!("{n} donuts left!"); }
+        Dessert::Candies(ref mut n) => { *n -= 1; println!("{n} candies left!"); }

With this, the output is now as expected from Rust Playground:

9 candies left!
8 candies left!
7 candies left!

Similarly, we can apply the same technique for an object that does not implement Copy trait, as in

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

enum Dessert{
  Donuts(String),
  Candies(String),
}

fn eat(dessert: Rc<RefCell<Dessert>>) {
    match *dessert.borrow_mut() {
        Dessert::Donuts(ref mut shape) => { *shape += "oO"; println!("{}", shape); }
        Dessert::Candies(ref mut shape) => { *shape += ">O< "; println!("{}", shape); }
    }
}

fn main() {
    let candy = Rc::new(RefCell::new(Dessert::Candies("".into())));
    eat(Rc::clone(&candy));
    eat(Rc::clone(&candy));
    eat(Rc::clone(&candy));
}

which prints out

>O<
>O< >O<
>O< >O< >O<