我刚刚编写了一个小的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
。而且我知道这不是递归调用的特殊情况,因为如果我定义一个与 fib
几乎相同的函数 fib2
,它们可以相互递归调用,也能正常工作。
为什么我不需要显式地借用一个借用的可变变量?