Я только что написал небольшую программу на Rust, которая вычисляет числа Фибоначчи и запоминает вычисления. Это работает, но я немного не понимаю, почему, особенно рекурсивный вызов. (Это также, вероятно, не идиоматично.)
Вот программа:
use std::collections::HashMap;
fn main() {
let n = 42; // hardcoded for simplicity
let mut cache = HashMap::new();
let answer = fib(n, &mut cache);
println!("fib of {} is {}", n, answer);
}
fn fib(n: i32, cache: &mut HashMap<i32,i32>) -> i32 {
if cache.contains_key(&n) {
return cache[&n];
} else {
if n < 1 { panic!("must be >= 1") }
let answer = if n == 1 {
0
} else if n == 2 {
1
} else {
fib(n - 1, cache) + fib(n - 2, cache)
};
cache.insert(n, answer);
answer
}
}
Вот как я понимаю, что происходит:
- В
main
let mut cache
означает «Я хочу иметь возможность видоизменить эту хеш-карту (или переназначить переменную)». - Когда
main
вызываетfib
, он передает&mut cache
, чтобы сказать: «Я одолжил вам это, и вам разрешено его видоизменять». - В подписи
fib
cache: &mut Hashmap
означает «Я ожидаю, что мне одолжат изменяемую HashMap - одолжу ее с разрешением на изменение».
(Пожалуйста, поправьте меня, если я ошибаюсь.)
Но когда fib
рекурсивно вызывает fib(n -1, cache)
, мне не нужно использовать fib(n -1, &mut cache)
, и если я это сделаю, я получаю сообщение об ошибке: «не могу заимствовать неизменяемую локальную переменную cache
как изменяемую». Хм? Это не неизменяемая локальная переменная, это изменяемое заимствование, верно?
Если я попробую fib(n - 1, &cache)
, я получу немного другую ошибку:
error: mismatched types:
expected `&mut std::collections::hash::map::HashMap<i32, i32>`,
found `&&mut std::collections::hash::map::HashMap<i32, i32>`
Похоже, он говорит: «Я ожидал изменяемую ссылку и получил ссылку на изменяемую ссылку».
Я знаю, что fib
дает взаймы в рекурсивном вызове, потому что, если он откажется от владения, он не сможет впоследствии вызвать cache.insert
. И я знаю, что это не особый случай рекурсии, потому что, если я определю, что fib2
почти идентичен fib
, я могу заставить их рекурсивно проходить друг через друга, и это будет работать нормально.
Почему мне не нужно явно передавать заимствованную изменяемую переменную?