Search code examples
rustpattern-matchingdestructuringmutableborrow-checker

Is it possible to have mutable binding and destructuring simultaneously?


The folllowing code works as expected (demo) but this requires two nested match {} which I want to replace with just one.

#![allow(unused)]
use std::collections::hash_map::HashMap;

#[derive(Debug, Clone)]
struct NormalTransaction(f64);

//  only normal transaction can be disputed.. BY DESIGN
#[derive(Debug, Clone)]
struct DisputeTransaction(NormalTransaction);

#[derive(Debug, Clone)]
enum Transaction {
    Normal(NormalTransaction),
    Dispute(DisputeTransaction),
}

fn main() {
    let mut m = HashMap::new();
    m.insert(1, Transaction::Normal(NormalTransaction(100.0)));
    
    println!("before: {:#?}", m);
    
    match m.get_mut(&1) {
        Some(t) => match t {
            Transaction::Normal(normal) => {
                *t = Transaction::Dispute(DisputeTransaction(normal.clone()));
            }   
            _ => {}, // NA
        }
        _ => {}, // NA
    }
    
    println!("after: {:#?}", m);
}

Replacing the two match{} with just one, as follows:

    match m.get_mut(&1) {
        Some(t @ Transaction::Normal(normal)) => {
            *t = Transaction::Dispute(DisputeTransaction(normal.clone()));
        }
        _ => {}, // NA
    }

but it does not work, saying (demo):

error: borrow of moved value
  --> src/main.rs:24:14
   |
24 |         Some(t @ Transaction::Normal(normal)) => {
   |              -^^^^^^^^^^^^^^^^^^^^^^^------^
   |              |                       |
   |              |                       value borrowed here after move
   |              value moved into `t` here
   |              move occurs because `t` has type `&mut Transaction` which does not implement the `Copy` trait

error[E0382]: borrow of moved value
  --> src/main.rs:24:38
   |
24 |         Some(t @ Transaction::Normal(normal)) => {
   |              ------------------------^^^^^^-
   |              |                       |
   |              |                       value borrowed here after move
   |              value moved here
   |
   = note: move occurs because value has type `&mut Transaction`, which does not implement the `Copy` trait
help: borrow this field in the pattern to avoid moving the value
   |
24 |         Some(ref t @ Transaction::Normal(normal)) => {
   |              +++

Is it possible to do the mutable binding and destructuring in single match case?


Solution

  • I don't know of a way to do it using a single match, but it doesn't look that bad if you use if lets instead of matches:

    if let Some(t) = m.get_mut(&1) {
        if let Transaction::Normal(normal) = t {
            *t = Transaction::Dispute(DisputeTransaction(normal.clone()));
        }   
    }
    

    Once let-chains lands I believe you should also be able to write

    if let Some(t) = m.get_mut(&1) && let Transaction::Normal(normal) = t {
        *t = Transaction::Dispute(DisputeTransaction(normal.clone()));
    }