为什么我不需要显式借出一个借来的可变变量?
Why do I not need to explicitly lend a borrowed, mutable variable?
我刚刚编写了一个小的 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
表示"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
递归调用fib(n -1, cache)
时,我不需要使用fib(n -1, &mut cache)
,如果我这样做我会得到一个错误:"cannot borrow immutable local variable cache
as mutable"。嗯?它不是一个不可变的局部变量,它是一个可变的借用 - 对吗?
如果我尝试 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>`
这看起来像是在说 "I expected a mutable reference and got a reference to a mutable reference"。
我知道 fib
正在递归调用中借出,因为如果它放弃所有权,它就无法在之后调用 cache.insert
。而且我知道这不是递归的特例,因为如果我将 fib2
定义为与 fib
几乎相同,我可以让它们相互递归并且它工作正常。
为什么我不需要显式借出一个借用的可变变量?
你的三分非常准确。当编译器不允许你传递 &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>
.
在您的函数中,cache
是外部 HashMap
的 引用。如果你仍然写& mut cache
,你试图改变一个参考[=18=],而不是原来的HashMap
。
我刚刚编写了一个小的 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
表示"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
递归调用fib(n -1, cache)
时,我不需要使用fib(n -1, &mut cache)
,如果我这样做我会得到一个错误:"cannot borrow immutable local variable cache
as mutable"。嗯?它不是一个不可变的局部变量,它是一个可变的借用 - 对吗?
如果我尝试 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>`
这看起来像是在说 "I expected a mutable reference and got a reference to a mutable reference"。
我知道 fib
正在递归调用中借出,因为如果它放弃所有权,它就无法在之后调用 cache.insert
。而且我知道这不是递归的特例,因为如果我将 fib2
定义为与 fib
几乎相同,我可以让它们相互递归并且它工作正常。
为什么我不需要显式借出一个借用的可变变量?
你的三分非常准确。当编译器不允许你传递 &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>
.
在您的函数中,cache
是外部 HashMap
的 引用。如果你仍然写& mut cache
,你试图改变一个参考[=18=],而不是原来的HashMap
。