为啥我不需要显式借出一个借来的可变变量?

Posted

技术标签:

【中文标题】为啥我不需要显式借出一个借来的可变变量?【英文标题】:Why do I not need to explicitly lend a borrowed, mutable variable?为什么我不需要显式借出一个借来的可变变量? 【发布时间】:2015-08-05 18:58:54 【问题描述】:

我刚刚编写了一个小的 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 时,它通过&amp;mut cache 说“我借给你这个,你可以改变它。” 在fib 的签名中,cache: &amp;mut Hashmap 的意思是“我希望被借出一个可变的 HashMap - 借用它并获得变异许可”

(如果我错了,请纠正我。)

但是当fib 递归调用fib(n -1, cache) 时,我不需要使用fib(n -1, &amp;mut cache),如果这样做我会得到一个错误:“不能借用不可变的局部变量cache as mutable”。嗯?它不是一个不可变的局部变量,而是一个可变的借用 - 对吧?

如果我尝试fib(n - 1, &amp;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 几乎相同,我可以让它们相互递归并且效果很好。

为什么我不需要显式借出一个借来的可变变量

【问题讨论】:

你可以传递所有权,并让函数返回一个带有答案和缓存的元组:PlayPen 【参考方案1】:

你的三分非常准确。当编译器不允许您传递&amp;mut cache 时,这是因为该值实际上已经被借用了。 cache 的类型是&amp;mut HashMap&lt;i32, i32&gt;,因此传递&amp;mut cache 会产生&amp;mut &amp;mut HashMap&lt;i32, i32&gt; 类型的值。只需传递 cache 就会得到预期的类型。

触发特定错误消息cannot borrow immutable local variable cache as mutable,因为变量cache 本身不是可变的,即使它指向的内存(HashMap)是可变的。这是因为参数声明 cache: &amp;mut HashMap&lt;i32, i32&gt; 没有声明 mut 变量。这类似于let 在可变性方面与let mut 的不同之处。 Rust 确实支持可变参数,在这种情况下看起来像 mut cache: &amp;mut HashMap&lt;i32, i32&gt;

【讨论】:

嗯。我不知道我可以在不可变变量中有一个可变值。你确定吗? let mut cache = 使两者都可变 - 有没有办法只声明结构可变?另外,如果我将cache = &amp;mut HashMap::new(); 添加到fib 的顶部,我会得到一个生命周期错误,但不会出现“你不能重新分配这个变量”错误。 另外:有趣的是“这是一个可变借用”是通过一个类型来检查的。谢谢你帮助我理解。 :) @NathanLong 您确实可以在不可变变量中拥有可变引用。这是因为 Rust 只需要可变引用是唯一的:没有其他代码可以读取或修改该值。它是编译器借用检查器的一部分,以确保所有&amp;mut 都保证是唯一的(&amp; 不需要相同)。由于变量始终是唯一的(因为您拥有它们),所以不可变的 &amp;mut 引用仍然是唯一的,因此可以对其进行修改。您可能还想查看this。 :) 啊,当然。 main 可能有一个 it 打算改变的哈希值,但将它不可变地借给print - “我只是允许你阅读这个,而不是修改它”。【参考方案2】:

在您的函数中,cache 是外部HashMap引用。如果你仍然写&amp; mut cache你是在尝试改变一个引用,而不是原来的HashMap

【讨论】:

以上是关于为啥我不需要显式借出一个借来的可变变量?的主要内容,如果未能解决你的问题,请参考以下文章

为啥需要显式定义静态变量?

如果从数组中复制最终变量,为啥 Java 需要对最终变量进行显式强制转换?

如果变量类型没有显式声明,为啥在以下情况下需要可选链接? [复制]

DDD 中的值对象 - 为啥是不可变的?

为啥我不需要在 gcc 4.6 中包含 STL 标头?

为啥在具有多个接口() 的对象中实现 QueryInterface() 时我需要显式向上转换