match a partially moved enum Rust

75 Views Asked by At

I am encountering an issue where I am consuming the contents of an enum by moving a value from it based on the a match over it. After doing this, I want to return that calculated value and also another value just based on which match the enum is. However, I am getting an error use of partially moved value: <variable name>. I know that I can just move everything into 1 match statement but that just isn't as clean in my more complicated case.

This is a minimum reproducible example of my issue. I want to do something like this:

enum Hand {
    TwoPair(String, String),
    ThreeOfAKind(String),
}



fn print_hand(hand: Hand) {
    let value = match hand {
        Hand::TwoPair(a, b) => a.parse::<i32>().unwrap() + b.parse::<i32>().unwrap(),
        Hand::ThreeOfAKind(a) => a.parse::<i32>().unwrap(),
    };
    match hand {
        Hand::TwoPair(_, _) => println!("Two pair: {}", value),
        Hand::ThreeOfAKind(_) => println!("Three of a kind: {}", value),
    }
}

but in order to get it to actally work right now, I would have to do something like:

fn print_hand_ugly(hand: Hand) {
    match hand {
        Hand::TwoPair(a, b) => {
            let value = a.parse::<i32>().unwrap() + b.parse::<i32>().unwrap();
            println!("Two pair: {}", value);
        }
        Hand::ThreeOfAKind(a) => {
            let value = a.parse::<i32>().unwrap();
            println!("Three of a kind: {}", value);
        }
    }
}

any help or tips would be appreciated. Copying the value is not possible in my situation, moving is very much the preferred case. MRE in Rust Playground

2

There are 2 best solutions below

0
Kyler Chin On

You can do this:

enum Hand {
    two_pair(String, String),
    three_of_a_kind(String),
}

fn print_hand(hand: Hand) {
    let value = match &hand {
        Hand::two_pair(a, b) => a.parse::<i32>().unwrap() + b.parse::<i32>().unwrap(),
        Hand::three_of_a_kind(a) => a.parse::<i32>().unwrap(),
    };
    match hand {
        Hand::two_pair(_, _) => println!("Two pair: {}", value),
        Hand::three_of_a_kind(_) => println!("Three of a kind: {}", value),
    }
}

This compiles because match uses &Hand rather than consuming the value itself. This means the values inside the match arm will be &String. In your original example, the match statement consumes and takes overship of hand, thus it is no longer avaliable to be used by anything after the value.

However, since this is just a print function, we can make the entire function borrow hand and not take ownership, like so:

fn print_hand(hand: &Hand) {
    let value = match hand {
        Hand::two_pair(a, b) => a.parse::<i32>().unwrap() + b.parse::<i32>().unwrap(),
        Hand::three_of_a_kind(a) => a.parse::<i32>().unwrap(),
    };
    match hand {
        Hand::two_pair(_, _) => println!("Two pair: {}", value),
        Hand::three_of_a_kind(_) => println!("Three of a kind: {}", value),
    }
}

Your original print function steals ownership of hand, but making hand have the type &Hand means that a reference is given instead, so ownership is not taken.

Ownership and borrowing is well explained in the Rust book: https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html

P.S. You can easily implement the Display trait instead https://doc.rust-lang.org/rust-by-example/hello/print/print_display.html This will make printing easier.

1
André Luís de Andrade On

Use the ref keyword to avoid moving your strings:

enum hand {
    two_pair(String, String),
    three_of_a_kind(String),
}

fn print_hand(hand: hand) {
    let value = match hand {
        hand::two_pair(ref a, ref b) => a.parse::<i32>().unwrap() + b.parse::<i32>().unwrap(),
        hand::three_of_a_kind(ref a) => a.parse::<i32>().unwrap(),
    };
    match hand {
        hand::two_pair(_, _) => println!("Two pair: {}", value),
        hand::three_of_a_kind(_) => println!("Three of a kind: {}", value),
    }
}