pixunil
pixunil

Reputation: 731

How can I introduce a copied variable as mutable in a if-let statement?

I have a HashMap<i8, i8> which could contain cycles:

let mut x: HashMap<i8, i8> = HashMap::new();
x.insert(1, 6);
x.insert(3, 5);
x.insert(5, 1);

To get the final value for 3, it should first lookup x[3], then x[5] and finally x[1] which should yield 6. I decided to use a while let loop:

let mut y = x[&3]; // y: i8
while let Some(&z) = x.get(&y) {
    y = z;
}

println!("{}", y);

x.insert(0, 0);

This works fine, but it would panic! if 3 is not in the map. As I don't want to do anything about the None case, I want to use a if let (similar to the while let used).

I have tried some notations:

  1. if let Some(&y) = x.get(&3): copies the value, but y is immutable (y: i8)
  2. if let Some(mut y) = x.get(&3): y is mutable, but the value is borrowed (mut y: &i8)
  3. if let mut Some(&y) = x.get(&3): my target: mutable copy, but invalid syntax (mut y: i8)

(All variants are available at Rust Playground, but you need to comment out the third try, as it is invalid syntax)

I would not argue about the second variant, but I need to insert values into my map in the body of the if let. As the map remains borrowed, I can't insert anymore. All I would need is that the value in Some(y) is copied, and y is mutable, so that the borrow checker is satisfied and I can do my recursive lookups.

Upvotes: 5

Views: 6731

Answers (2)

user4815162342
user4815162342

Reputation: 155465

Your approach #1 is a perfectly correct match, you just need to make the y variable mutable. One possibility is to convert Option<&i8> to Option<i8>, enabling the use of mut y in the pattern. For example, Option::map can dereference the value:

if let Some(mut y) = x.get(&3).map(|ref| *ref) {

Since Copy implies (cheap) Clone, you can express the same using Option::cloned():

if let Some(mut y) = x.get(&3).cloned() {

As of Rust 1.35, you can use Option::copied(), which is guaranteed to just copy the value (and fails to compile if the value is not Copy):

if let Some(mut y) = x.get(&3).copied() {

Another possibility is to leave your approach #1 as-is, but correct it simply by introducing a separate mutable variable inside the if let block:

if let Some(&y) = x.get(&3) {
    let mut y = y;
    ...

Upvotes: 8

Shepmaster
Shepmaster

Reputation: 432019

Your code basically works:

use std::collections::HashMap;

fn main() {
    let mut x: HashMap<i8, i8> = HashMap::new();
    x.insert(1, 6);
    x.insert(3, 5);
    x.insert(5, 1);

    let mut key = 3;
    while let Some(&z) = x.get(&key) {
        key = z;
    }

    println!("{}", key);

    x.insert(key, 0);
}

Here, key is left as the last key that did not match.

Upvotes: 2

Related Questions