我刚刚写了一个小的Rust程序,它计算Fibonacci数并记住计算 . 它有效,但我有点困惑为什么,特别是递归调用 . (它也可能不是惯用的 . )
这是程序:
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
表示"I want to be able to mutate this hashmap (or re-assign the variable)" . -
当
main
调用fib
时,它传递&mut cache
来说"I'm lending you this, and you're allowed to mutate it." -
在
fib
的签名中,cache: &mut Hashmap
表示"I expect to be lent a mutable HashMap - to borrow it with permission to mutate"
(如果我错了,请纠正我 . )
但是当 fib
recurses,调用 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
几乎相同,我可以让它们相互递归并且它可以正常工作 .
Why do I not need to explicitly lend a borrowed, mutable variable ?
1 回答
你的三点非常重要 . 当编译器不允许您传递
&mut cache
时,这是因为该值实际上已经被借用了 .cache
的类型是&mut HashMap<i32, i32>
,因此传递&mut cache
会产生类型为&mut &mut HashMap<i32, i32>
的值 . 只是传递cache
结果是预期的类型 .触发特定错误消息
cannot borrow immutable local variable cache as mutable
,因为变量cache
本身不可变,即使它指向的内存(HashMap
)是 . 这是因为参数声明cache: &mut HashMap<i32, i32>
没有声明mut
变量 . 这类似于let
在可变性方面与let mut
的区别 . Rust确实支持可变参数,在这种情况下看起来像mut cache: &mut HashMap<i32, i32>
.