简体   繁体   中英

Borrow checker complains when adding memoization to a function

I want to add memoization to a function which can be kind of costly. I tried to make a minimum example below which demonstrates the error.

use std::collections::{HashMap};

struct Foo {
    data: i64,
    memo: HashMap<i64, String>
}

impl Foo {
    fn new(data: i64) -> Foo {
        let memo = HashMap::new();
        Foo {data, memo}
    }

    fn f(&self, x: i64) -> String {
        (self.data + x).to_string()
    }

    fn f_cached(&mut self, x: i64) -> String {
        match self.memo.get(&x) {
            Some(result) => result.clone(),
            None => {
                let result = self.f(x);
                self.memo.insert(x, result.clone());
                result
            }
        }
    }
}

I remove the f_cached function below then everything compiles, but the cached version refuses to compile; the error I get is:

error[E0502]: cannot borrow `self.memo` as mutable because it is also borrowed as immutable
   --> src/main.rs:173:17
    |
169 |         match self.memo.get(&x) {
    |               --------- immutable borrow occurs here
...
173 |                 self.memo.insert(x, result.clone());
    |                 ^^^^^^^^^ mutable borrow occurs here
...
176 |         }
    |         - immutable borrow ends here

I've tried various reworkings of the basic logic, such as assigning self.memo.get(&x) to a variable, using if let , etc etc, but everything produces the same or a different kind of error. Certainly it doesn't look like there's anything unsafe going on in the code.

While other references are correct, note that in the nightly version of the compiler a feature called non-lexical lifetimes is available, which, when enabled, makes your code compilable as it is:

#![feature(nll)]

use std::collections::{HashMap};

struct Foo {
    data: i64,
    memo: HashMap<i64, String>
}

impl Foo {
    fn new(data: i64) -> Foo {
        let memo = HashMap::new();
        Foo {data, memo}
    }

    fn f(&self, x: i64) -> String {
        (self.data + x).to_string()
    }

    fn f_cached(&mut self, x: i64) -> String {
        match self.memo.get(&x) {
            Some(result) => result.clone(),
            None => {
                let result = self.f(x);
                self.memo.insert(x, result.clone());
                result
            }
        }
    }
}

fn main() {}

Playground

The tracking issue for the NLL feature is here . It is currently in active development and probably won't be stable in the nearest future, but it is the direction where Rust is going, and if you're okay with using the nightly version of the compiler, you can just use #![feature(nll)] .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM