Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unexpected borrowcheck error when using Cow<> #82146

Closed
emilio opened this issue Feb 15, 2021 · 2 comments
Closed

Unexpected borrowcheck error when using Cow<> #82146

emilio opened this issue Feb 15, 2021 · 2 comments

Comments

@emilio
Copy link
Contributor

emilio commented Feb 15, 2021

It seems to me this ought to compile, or at least it's not clear to me why it doesn't. The following code:

use std::collections::HashMap;
use std::borrow::Cow;

pub type Cache = HashMap<i32, i32>;

fn compute<'cache>(input: i32, cache: &'cache mut Cache) -> Cow<'cache, i32> {
    if let Some(cached) = cache.get(&input) {
        return Cow::Borrowed(cached)
    }
    
    let result = input + 2;
    cache.insert(input, result);
    Cow::Borrowed(&cache[&input])
}

fn main() {}

Yields:

error[E0502]: cannot borrow `*cache` as mutable because it is also borrowed as immutable
  --> src/lib.rs:12:5
   |
6  | fn compute<'cache>(input: i32, cache: &'cache mut Cache) -> Cow<'cache, i32> {
   |            ------ lifetime `'cache` defined here
7  |     if let Some(cached) = cache.get(&input) {
   |                           ----- immutable borrow occurs here
8  |         return Cow::Borrowed(cached);
   |                --------------------- returning this value requires that `*cache` is borrowed for `'cache`
...
12 |     cache.insert(input, result);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0502`.

Obviously my actual code is more complicated than this (it computes multiple outputs in the same non-cached path, might not cache the input, etc), so using .entry() etc is not really an option.

What am I missing?

@SkiFire13
Copy link
Contributor

SkiFire13 commented Feb 16, 2021

Looks like a duplicate of #54663

I think the fix for now is changing that if let to something like:

if cache.contains_key(&input) {
    match cache.get(&input) {
        Some(cached) => return Cow::Borrowed(cached),
        None => unreachable!(),
    }
}

However this will probably be slower than it should be.

The definitive solution will probably be the polonius borrow checker but it isn't stable nor ready yet.

@emilio
Copy link
Contributor Author

emilio commented Feb 16, 2021

Yeah, indeed, looks like a dupe. Slightly shorter workaround:

if cache.contains_key(&input) {
    return Cow::Borrowed(&cache[&input]);
}

A bit baffled that the fast thing doesn't work but for my use case that workaround is probably good enough.

@emilio emilio closed this as completed Feb 16, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants