Why can I match on the result of HashMap::get by both value and reference?

Viewed 145

Why does Rust allow both the match with Some(v) and Some(&v) in the following examples?

let mut dict1: HashMap<String, i32> = HashMap::new();
let word: String = String::new();

let get1 = dict1.get(word); 
match get1 {
    Some(v) => {
        value = *v;
        println!("Key returned is {}", value);
    }
    None => {}
}
let mut dict1: HashMap<String, i32> = HashMap::new();
let word: String = String::new();

let get1 = dict1.get(word);
match get1 {
    Some(&v) => {
        value = v;
        println!("Key returned is {}", value);
    }
    None => {}
}
1 Answers
match get1 {
    Some(v) => {
        value = *v;
        println!("Key returned is {}", value);
    }
    None => {}
}

In the first match, Some(v) matches with get1 structure which is Some(&i32). So, v type is &i32.

match get1 {
    Some(&v) => {
        value = *v;
        println!("Key returned is {}", value);
    }
    None => {}
}

In the second match, Some(&v) matches with get1 structure which is Some(&i32). in this case, v type is i32. In Some(&v) pattern, the &v is called a reference pattern (see docs)

As suggested by Sven Marnach, v type needs to be Copy, because the default binding mode in match arm follows move semantic.

The following snippet wont compile because String is not Copy.

let get_string = &String::from("meow");
let get1 = Some(meow);

match get1 {
    Some(&v) => {
        value = *v;
        println!("Key returned is {}", value);
    }
    None => {}
}

Just to be precise, it is the binding which requires v to be Copy, not the reference pattern.

You can see that the following code example use reference pattern on a non Copy type and compiles, because there is no binding.

fn main() {
    let meow = &String::from("meow");
    let my_ref = Some(meow);
    
    match my_ref {
        // just reference pattern here, no binding
        s @ Some(& _) => {println!("{:?}", s)}
        _ =>()
    };
}
Related