2

I'm struggling a bit with variable scoping. I currently have something like this code:

use std::collections::HashMap;

fn main() {
    let mut cache: HashMap<usize, usize> = HashMap::new();

    fn fib(n: usize) -> usize {
        // Special values
        if n == 0 || n == 1 {
            return 1;
        }

        // Check if value is in cache
        if let Some(&a) = cache.get(&n) {
            return a;
        }

        // Calculate
        let f = fib(n - 2) + fib(n - 1);

        // Insert in cache for later use
        cache.insert(n, f);

        return f;
    }

    println!("The 11th Fibonacci number is: {}", fib(10));
}

I want to generate Fibonacci numbers, but also use a cache to skip recalculating the same items. The actual code does some heavier calculating, but also uses recursion.

However, trying to compile this, I get a can't capture dynamic environment in a fn item; use the || { ... } closure form instead warning at cache.get and cache.insert. Applying this closure form to that code:

use std::collections::HashMap;

fn main() {
    let mut cache: HashMap<usize, usize> = HashMap::new();

    let fib = |n: usize| -> usize {
        // Special values
        if n == 0 || n == 1 {
            return 1;
        }

        // Check if value is in cache
        if let Some(&a) = cache.get(&n) {
            return a;
        }

        // Calculate
        let f = fib(n - 2) + fib(n - 1);

        // Insert in cache for later use
        cache.insert(n, f);

        return f;
    };

    println!("The 11th Fibonacci number is: {}", fib(10));
}

Fixed the cache error, but gives a cannot find function `fib` in this scope warning at let f = ....

I have also tried to use an environment as stated in Is it possible to make a recursive closure in Rust?, but that didn't like that I called the same function twice, thus borrowing the environment twice while the environment has a mutable cache in it.

How would I handle this weird case?

Community
  • 1
  • 1
Charlie
  • 978
  • 1
  • 7
  • 27
  • *I have also tried to use an environment ... but that didn't like that I called the same function twice*. Except you didn't **show us** this attempt and [it appears to work](http://play.integer32.com/?gist=c242d071ac196a06fc7cdd9384287e88&version=stable). – Shepmaster Feb 17 '17 at 23:00

1 Answers1

4

You were on the right track with using environments, but you need to make sure everything that needs to be mutable is mutable:

use std::collections::HashMap;

fn main() {
    struct FibEnv { cache: HashMap<usize, usize> }

    fn fib(mut env: &mut FibEnv, n: usize) -> usize {
        // Special values
        if n == 0 || n == 1 {
            return 1;
        }

        // Check if value is in cache
        if let Some(&a) = env.cache.get(&n) {
            return a;
        }

        // Calculate
        let f = fib(&mut env, n - 2) + fib(&mut env, n - 1);

        // Insert in cache for later use
        env.cache.insert(n, f);

        return f;
    }

    let cache: HashMap<usize, usize> = HashMap::new();
    let mut env = FibEnv { cache: cache };
    println!("The 11th Fibonacci number is: {}", fib(&mut env, 10));
}

As @Shepmaster stated in the comments, the following is even easier:

use std::collections::HashMap;

fn main() {
    fn fib(cache: &mut HashMap<usize, usize>, n: usize) -> usize {
        // Special values
        if n == 0 || n == 1 {
            return 1;
        }

        // Check if value is in cache
        if let Some(&a) = cache.get(&n) {
            return a;
        }

        // Calculate
        let f = fib(cache, n - 2) + fib(cache, n - 1);

        // Insert in cache for later use
        cache.insert(n, f);

        return f;
    }

    let mut cache: HashMap<usize, usize> = HashMap::new();
    println!("The 11th Fibonacci number is: {}", fib(&mut cache, 10));
}
Charlie
  • 978
  • 1
  • 7
  • 27
  • At least one too many `mut` in your first example: the parameter `env` need not be `mut`, it's never assigned to. – Matthieu M. Feb 20 '17 at 08:25