Why can't these fields be accessed?

Hello again,

first the enum in question:

pub enum Exp {
    Int {
        val: i32,
        b: bool
    },
    Plus {
        e1: Box<Exp>,
        e2: Box<Exp>,
        b: bool
    },
  
    Mult{
        e1: Box<Exp>,
        e2: Box<Exp>,
        b: bool
    },
}

In the following pretty function I want to set the bool b of e1 and e2 to true whenever the Multiplication has been visited. (so for example (1 + 2) * 2 keeps it paranthesis but 1 + 2 * 2 stays the same too).

pub fn pretty(mut self) -> String {
        return match self {
            Exp::Int { val, mut b } => {
                val.to_string()
            },

            Exp::Plus { e1, e2, mut b } => {
                println!("b: {}", b);
                if b { //if true Mult has been visited
                    let s = format!("s: ( {} + {} )", e1.pretty(), e2.pretty());
                    s.to_string()
                }
                else { //if false Mult has not been visited
                    let s = format!("s: {} + {}", e1.pretty(), e2.pretty());
                    s.to_string()
                }
            },

            Exp::Mult { mut e1, mut e2, mut b } => {
                e1.set(true);
                e2.set(true);

                let s = format!( "{} * {}", e1.pretty(), e2.pretty());
                s.to_string()
            }

So by calling set on e1 and e2 I thought it would change it:

pub fn set(&mut self, set_to: bool) -> (){
        return match self{
            Exp::Int { val,  mut b} => {
                b = set_to
            }
            Exp::Plus { e1, e2, mut b} => {
                println!("plus");
                b = set_to
            }
            Exp::Mult { e1, e2, mut b} => {
                println!("mult");
                //b = set_to
            }
        }
    }

My question:
Is it even correct, that when I call set on e1 (or e2) e1 is the self given to set() as a mutable reference including all it's fields?
Because when I looked into it with the Debugger all the expressions where in their correct places. It even prints the correct output just without the parenthesis. And when I initially set the bool in the ::Plus expressions to true it correctly prints the "unnecessary" parenthesis too. So the error must be in how I try to access the bool fields.

Kind regards,
naili

Here b is a variable of type &mut bool, a reference or pointer to a bool. When you do b = set_to, you're trying to change the value of the local variable b, not the value pointed to by b. To change the pointed-to value, replace the line as follows:

-                 b = set_to;
+                 *b = set_to;

And similarly with the other arms in that match.

I'm kind of surprised here because the compiler should have given you a type error on the line b = set_to, since the types on either side of = do not agree. Did you get an error like that? Is this the exact code you were stepping through with a debugger?

1 Like

mut resets the match binding mode, so b here is in fact copied out by value. It's a significant failure of match "ergonomics".

Try this:

    pub fn set(&mut self, set_to: bool) -> (){
        return match self {
            Exp::Int { val, ref mut b} => {
//                          ^^^
                *b = set_to
//              ^
            }

Playground.

6 Likes

Ugh, now I'm definitely adding #![deny(clippy::pattern_type_mismatch)] to all my projects.

3 Likes

So

Exp::Int { val, &mut b}

would be reset, because the pattern match sees the & as part of what is matched and then consumes it? And ref forces the value to stay a reference and it's not part of the pattern?

It all works now :slight_smile:
This is just a question to make sure I understand how ref and & behave during pattern match.
Thank you very much!

Note that – at least in this case – the code does manage to produce warnings like

warning: variable `b` is assigned to, but never used
  --> src/main.rs:23:33
   |
23 |             Exp::Int { val, mut b} => {
   |                                 ^
   |
   = note: consider using `_b` instead

warning: value assigned to `b` is never read
  --> src/main.rs:24:17
   |
24 |                 b = set_to
   |                 ^
   |
   = note: `#[warn(unused_assignments)]` on by default
   = help: maybe it is overwritten before being read?

I’m not sure why clippy doesn’t have a lint for disallowing any mut/ref in a match ergonomics pattern.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.