Reputation: 986
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?
Upvotes: 1
Views: 979
Reputation: 986
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));
}
Upvotes: 3