Dereferencing a borrow makes mutable variable immutable?

Hi guys, I'm a newbie to Rust,
there is a problem, which makes me struggled for 2 days:

I want to write a terminal application to print the Fibonacci sequence from 1 to N,
and I did it, after that, I want to add a cache to it, and here is the code:

use std::env;
use std::collections::HashMap;

fn main() {
    let n = env::args().nth(1).expect("No n specify.");
    let n: isize = n.parse()
        .expect("N should be a number.");
    let cache: &mut HashMap<String, String> = &mut HashMap::new();
    
    println!("Input n: {}", n);
    for i in 1 .. n + 1 {
        println!("{}", fib(i, cache));
    }
}

fn fib(n: isize, mut cache: &mut HashMap<String, String>) -> isize {
    let key = n.to_string();
    match cache.get(&key) {
        Some(v) =>  {
                let r: isize = v.parse()
                    .expect("Parse error");
                r
            },
        None => {
            let mut result;
            if n == 0 {
                result = 0;
            } else if n == 1 || n == 2 {
                result = 1;
            } else {
                result = fib(n - 1, cache) + fib(n - 2, cache);
            }
            cache.insert(key, result.to_string());
            return result;
        }
    }
}

The code above has been modified many times because of the cache's mutability remains incorrect.
how should I fix it?

Yeah, the get() in the match statement keeps the borrow over cache for the entire match statement. The easiest way to change your code is (with a bit of other cleanup):

fn fib(n: isize, cache: &mut HashMap<String, String>) -> isize {
    let key = n.to_string();
    if let Some(v) = cache.get(&key) {
        return v.parse().expect("Parse error");
    }
    let result = if n == 0 {
        0
    } else if n == 1 || n == 2 {
        1
    } else {
        fib(n - 1, cache) + fib(n - 2, cache)
    };
    cache.insert(key, result.to_string());
    result
}

You should also avoid the String conversions and storage, and just switch to storing the integer value. In addition, isize is a slightly strange choice of integer type - are you sure you don't want a fixed-width one, like i32/i64?

1 Like

Thank you for the answer and advise :smiley:!
In the early version of the code, the HashMap is isize, but after many rounds of my "random fix", it comes that way :joy:
I know isize is strange, so I just want to have a try...

Ok, gotcha :slight_smile:

I should also note that your code should compile as-is with NLL (non-lexical lifetimes), which is an enhancement coming to Rust very soon. But it helps, IMO, to understand where things stand today.

1 Like