enumsrustmatchinginterior-mutability

What is the intended way to match an interior mutable enum?


These are the ways I could come up with to try matching a reference counted, interior mutable enum:

#![allow(unused)]

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

#[derive(Debug, Clone, PartialEq)]
struct Bar {
    some_bar: Vec<f64>,
}
#[derive(Debug, Clone, PartialEq)]
struct Baz {
    some_baz: i32,
}

#[derive(Debug, Clone, PartialEq)]
enum Foo {
    Bar(Bar),
    Baz(Baz),
}

fn is_baz(foo_ref: Rc<RefCell<Foo>>) -> bool {
    match foo_ref {
        Foo::Bar(_) => false,
        Foo::Baz(_) => true,
    }
}

fn is_baz_borrow(foo_ref: Rc<RefCell<Foo>>) -> bool {
    match foo_ref.borrow() {
        Foo::Bar(_) => false,
        Foo::Baz(_) => true,
    }
}

fn is_baz_deref(foo_ref: Rc<RefCell<Foo>>) -> bool {
    match *foo_ref {
        Foo::Bar(_) => false,
        Foo::Baz(_) => true,
    }
}

fn is_baz_get_mut(foo_ref: Rc<RefCell<Foo>>) -> bool {
    match foo_ref.get_mut() {
        Foo::Bar(_) => false,
        Foo::Baz(_) => true,
    }
}

fn is_baz_as_ref(foo_ref: Rc<RefCell<Foo>>) -> bool {
    match foo_ref.as_ref() {
        Foo::Bar(_) => false,
        Foo::Baz(_) => true,
    }
}

fn is_baz_as_ref_borrow(foo_ref: Rc<RefCell<Foo>>) -> bool {
    match foo_ref.as_ref().borrow() {
        Foo::Bar(_) => false,
        Foo::Baz(_) => true,
    }
}

fn is_baz_get_mut_mut(mut foo_ref: Rc<RefCell<Foo>>) -> bool {
    match foo_ref.get_mut() {
        Foo::Bar(_) => false,
        Foo::Baz(_) => true,
    }
}

fn main() {
    let foo = Foo::Bar(Bar {
        some_bar: vec![1.1, 999.0],
    });
    let foo_ref = Rc::new(RefCell::new(foo));

    // none of these work
    assert!(is_baz(foo_ref.clone()));
    assert!(is_baz_borrow(foo_ref.clone()));
    assert!(is_baz_deref(foo_ref.clone()));
    assert!(is_baz_get_mut(foo_ref.clone()));
    assert!(is_baz_as_ref(foo_ref.clone()));
    assert!(is_baz_as_ref_borrow(foo_ref.clone()));

    // this works
    assert!(is_baz_get_mut_mut(foo_ref.clone()));
}

Playground

However most ways create an mismatched types error like

error[E0308]: mismatched types
  --> src/main.rs:24:9
   |
24 |         Foo::Baz(_) => true
   |         ^^^^^^^^^^^ expected struct `std::rc::Rc`, found enum `Foo`
   |
   = note: expected type `std::rc::Rc<std::cell::RefCell<Foo>>`
              found type `Foo`

or

error[E0308]: mismatched types
  --> src/main.rs:30:9
   |
30 |         Foo::Bar(_) => false,
   |         ^^^^^^^^^^^ expected struct `std::cell::Ref`, found enum `Foo`
   |
   = note: expected type `std::cell::Ref<'_, Foo, >`
              found type `Foo`

The only way that worked is this:

match foo_ref.get_mut() {
    Foo::Bar(_) => false,
    Foo::Baz(_) => true
}

Is this the intended way? Even for read only access?


Solution

  • You can use the Deref impl of Ref:

    fn is_baz_get(foo_ref: Rc<RefCell<Foo>>) -> bool {
        match *foo_ref.borrow() {
            Foo::Bar(_) => false,
            Foo::Baz(_) => true
        }
    }