Rust 无法在递归斐波那契实现中将不可变的借用HashMap缓存借用为可变的

Rust 无法在递归斐波那契实现中将不可变的借用HashMap缓存借用为可变的,rust,mutable,borrow-checker,Rust,Mutable,Borrow Checker,我想实现一个斐波那契级数,同时缓存已经计算的结果。我不确定这种方法在生锈的情况下是否可行,但这是我想到的最好的方法。代码如下: use std::collections::HashMap; pub fn fib_hash(n: u32) -> u64 { let mut map: HashMap<u32, u64> = HashMap::new(); // This is the engine which recurses saving each value

我想实现一个斐波那契级数,同时缓存已经计算的结果。我不确定这种方法在生锈的情况下是否可行,但这是我想到的最好的方法。代码如下:

use std::collections::HashMap;

pub fn fib_hash(n: u32) -> u64 {
    let mut map: HashMap<u32, u64> = HashMap::new();

    // This is the engine which recurses saving each value in the map
    fn f(map: &HashMap<u32, u64>, n: u32) -> u64 {
        let c = match map.get(&n) {
            Some(&number) => number,
            _ => 0,
        };
        if c != 0 {
            return c;
        }
        let m = match n {
            1 if n < 1 => 0,
            1...2 => 1,
            _ => f(&map, n - 1) + f(&map, n - 2),
        };
        map.insert(n, m);
        m
    }
    f(&map, n)
}
Rust 2018

error[E0596]:无法将`*map`借用为可变的,因为它位于`&`引用后面
-->src/lib.rs:20:9
|
7 | fn f(映射:&HashMap,n:u32)->u64{
----------------帮助:考虑把这个变成一个可变的参考:'MUTSTD::集合:HASMAP ''
...
20 |地图插入(n,m);
|^^^`map`是一个`&`引用,因此它引用的数据不能作为可变数据借用

我可以在Rust中使用这种方法吗?这个问题的最佳解决方案是什么?

您将
map
参数声明为
f
,它是一个不可变的引用,只允许您调用
get
和其他不修改
HashMap
的函数。使用
&mut HashMap
>作为
map
的类型,需要一个允许突变的引用。这还要求您使用
&mut-map
而不是
&map
注释调用站点

就我个人而言,我会使用一种不同的方法,使用所有权转移而不是引用。但每个人都有自己的风格

pub fn fib_hash(n: u32) -> u64 {
    // This is the engine which recurses saving each value in the map
    fn f(map: HashMap<u32, u64>, n: u32) -> (HashMap<u32, u64>, u64) {
        if let Some(&number) = map.get(&n) {
            return (map, number);
        }
        let (map, a) = f(map, n - 1);
        let (mut map, b) = f(map, n - 2);
        let res = a + b;
        map.insert(n, res);
        (map, res)
    }
    let mut map = HashMap::new();
    map.insert(0, 0);
    map.insert(1, 1);
    map.insert(2, 1);
    f(map, n).1
}
pub fn fib_hash(n:u32)->u64{
//这是在映射中递归保存每个值的引擎
fn f(映射:HashMap,n:u32)->(HashMap,u64){
如果let Some(&number)=map.get(&n){
返回(地图、编号);
}
设(map,a)=f(map,n-1);
let(mut-map,b)=f(map,n-2);
设res=a+b;
地图.插入(n,res);
(地图、图片)
}
让mut map=HashMap::new();
图.插入(0,0);
地图.插入(1,1);
地图.插入(2,1);
f(地图,n)
}

诀窍是在参数列表中使
map
成为可变引用(并明确使用寿命):

使用std::collections::HashMap;
fn-fib-u32{
如果n和,
无=>{
让new_fib_sum=fib(n-1,memo)+fib(n-2,memo);
备注。插入(n,新的fib和);
新的fib和
}
};
}
fn main(){
让mut memo:HashMap=HashMap::new();
设n=10;//或用户输入…或其他
打印!(“{}”,fib(n,&mut-memo));
}

本书对此进行了解释:谢谢。我有
&mut HashMap
,但无法让它工作。我显然很快就放弃了。同样感谢alt解决方案。了解不同的方法是很有用的。碰巧,@ker,我已经将你的版本与我的版本进行了对比。看起来你的方法需要稍微长一点。在现实世界中没有任何价值,但可能很有趣。如果你想自己运行基准测试,你可以在上面签出我的代码。是的,对于这样一个简单的问题,我假设它会比较慢。这是编译器的职责:)您也可以使用
Vec
而不是
HashMap
,因为您知道,您将始终计算所有值,直到最终值,并且不会遗漏中间的值:,占用
HashMap
版本约20%的计算时间,但最有可能的是,如果没有尾部调用优化,计数版本将始终是最快的。在考虑效率时,肯定有更好的解决方案。我只是想优化递归算法。我想使用vec也可以。很好的建议。他的。。。已经是另一个答案所说的:使用
&mut HashMap
作为
map
的类型。
use std::collections::HashMap;

fn fib<'a>(n: u32, memo: &'a mut HashMap<u32, u32>) -> u32 {
    if n <= 2 {
        return 1;
    }

    return match memo.get(&n) {
        Some(&sum) => sum,
        None => {
            let new_fib_sum = fib(n - 1, memo) + fib(n - 2, memo);
            memo.insert(n, new_fib_sum);
            new_fib_sum
        }
    };
}

fn main() {
    let mut memo: HashMap<u32, u32> = HashMap::new();
    let n = 10; //or user input...or whatever
    print!("{}", fib(n, &mut memo));
}