Match arm type strangeness


#1

:wave: Hi all, I’ve been learning Rust by starting with a basic Piston set up and iterating on a simple game. I ran into a situation with using match, and while I ended up with a solution that works for me, I don’t quite understand the reason I had to do what I did. I’m hoping someone can explain what I’m missing.

So, I’m writing code to detect keyboard key press releases. I started out with this:

  fn release(&mut self, args: &Button) {                                                                                                                                                      
      if let Keyboard(button) = *args {                                                                                                                                                       
          match button {                                                                                                                                                                      
              Key::A => println!("Released A"),                                                                                                                                  
              Key::S => println!("Released S"),                                                                                                                                  
              Key::D => println!("Released D"),                                                                                                                                  
              Key::W => println!("Released W"),                                                                                                                                  
              _ => (),                                                                                                                                                                        
          }                                                                                                                                                                                   
      }                                                                                                                                                                                       
  }  

Okay, that worked great!

Next, I add a key_state field to self, choosing a key_state: HashSet<Key> type, which seemed good to me. In doing that, I’m going to add and remove keys to the key_state as they’re pressed and released, respectively, so that I can track the set of keys currently depressed. I want to do that to allow for diagonal movement and to bypass debouncing issues I’d been seeing.

My next iteration on the function is (focusing just on the match, that’s the only part changing):

          match button {                                                                                                                                                                      
              Key::A => self.key_state.remove(&button),                                                                                                                                  
              Key::S => self.key_state.remove(&button),                                                                                                                                  
              Key::D => self.key_state.remove(&button),                                                                                                                                  
              Key::W => self.key_state.remove(&button),                                                                                                                                  
              _ => (),                                                                                                                                                                        
          }                                                 

Building that, I get this error:

  error[E0308]: match arms have incompatible types                                                                                                                                            
    --> src/lib.rs:70:13                                                                                                                                                                      
     |                                                                                                                                                                                        
  70 | /             match button {                                                                                                                                                           
  71 | |                 Key::A => self.key_state.remove(&button),                                                                                                                            
  72 | |                 Key::S => self.key_state.remove(&button),                                                                                                                            
  73 | |                 Key::D => self.key_state.remove(&button),                                                                                                                            
  74 | |                 Key::W => self.key_state.remove(&button),                                                                                                                            
  75 | |                 _ => (),                                                                                                                                                             
  76 | |             }                                                                                                                                                                        
     | |_____________^ expected bool, found ()                                                                                                                                                
     |                                                                                                                                                                                        
     = note: expected type `bool`                                                                                                                                                             
                found type `()`                                                                                                                                                               
  note: match arm with an incompatible type                                                                                                                                                   
    --> src/lib.rs:75:22                                                                                                                                                                      
     |                                                                                                                                                                                        
  75 |                 _ => (),                                                                                                                                                               
     |          

Ah, of course, remove returns a bool, simple mistake. So, I change:

              _ => (),   

to:

              _ => false,   

Because I don’t really care about the returned value, I just want to avoid a long winded chain of if let statements. But, to my surprise, the compiler tells me:

  error[E0308]: mismatched types                                                                                                                                                              
    --> src/lib.rs:70:13                                                                                                                                                                      
     |                                                                                                                                                                                        
  70 | /             match button {                                                                                                                                                           
  71 | |                 Key::A => self.key_state.remove(&button),                                                                                                                            
  72 | |                 Key::S => self.key_state.remove(&button),                                                                                                                            
  73 | |                 Key::D => self.key_state.remove(&button),                                                                                                                            
  74 | |                 Key::W => self.key_state.remove(&button),                                                                                                                            
  75 | |                 _ => false,                                                                                                                                                          
  76 | |             }                                                                                                                                                                        
     | |_____________^ expected (), found bool                                                                                                                                                
     |                                                                                                                                                                                        
     = note: expected type `()`                                                                                                                                                               
                found type `bool`          

Wut? I went back and forth changing _ =>'s result between () and false, and it just kept oscillating errors on me. Eventually, I arrived at:

      fn release(&mut self, args: &Button) {                                                                                                                                                  
          if let Keyboard(button) = *args {                                                                                                                                                   
              use Key::*;                                                                                                                                                                     
              match button {                                                                                                                                                                  
                  A | S | D | W => { self.key_state.remove(&button); },                                                                                                                       
                  _ => (),                                                                                                                                                                    
              }                                                                                                                                                                               
          }                                                                                                                                                                                   
      } 

Which works great. Now, I understand that { self.key_state.remove(&button); } returns (), so that’s why it’s fine, but I don’t understand why my change to:

                  _ => false,   

Didn’t work. I’m pretty sure I’m just missing something subtle with the way the code’s evaluated, but I just can’t see it. Any help?


#2

I’m learning so this may be way off but I think it may have to do with the ‘;’ at the end of your self.key_state.remove(…) call which turns an expression (remove() that returns a bool) to a statement that returns nothing. In other words, the semicolon stops the return of the expression.
EDIT: in your last code example that is :slight_smile:


#3

Thinking further on this, maybe match only returns a Result or Option, not a bool so in that case if you {}'d your codes after the match it will return the right Type as per your last example.


#4

The match returns a bool and since there is no ; after the match, that bool is also returned by the if let and since there is no ; after that either, the bool is returned from the function itself.
But the function specifies not return type, which is equivalent to returning ().


#5

Ah, now I see it. I was failing to realize that the return value from the match was making it’s way all the way to the return of the function, causing an issue between what the match returned, and the declared return type of the function. I was misled by just previously getting the other error.

That all makes sense now, thanks for helping me to see the reason! :beers: