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<