1

Here is a simple program which looks up previous value in map and puts a new value into it. It won't compile as the first lookup immutably borrows map and insert wants to mutably borrow map.

use std::collections::HashMap;

fn main() {
    let mut map: HashMap<i32, i32> = HashMap::new();
    map.insert(0, 0);

    (1..5).for_each(|x| {
        let prev = map.get(&(x - 1)).unwrap();
        map.insert(x, (prev + 1) * 10);
    });

    map.iter().for_each(|(k, v)| println!("{} -> {}", k, v));
}

The problem can be solved by using clone() on to get prev as

let prev = map.get(&(x - 1)).unwrap().clone();

and get the output as:

0 -> 0
1 -> 10
2 -> 110
3 -> 1110
4 -> 11110

What can I do to not use clone() here?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Xolve
  • 22,298
  • 21
  • 77
  • 125
  • 1
    use a scope if you don't use NLL, https://play.integer32.com/?version=stable&mode=debug&edition=2015&gist=f2754b8bb3c0e02d357cd4fdbcb03d5a – Stargateur Mar 02 '19 at 05:17
  • The code you've provided [compiles as-is](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=fc4a7c9b6200933f4b615ad6e5667145). Upgrade your Rust version and switch to Rust 2018. – Shepmaster Mar 02 '19 at 18:44
  • @Stargateur Can you please post this as answer so that I can mark it as accepted answer. – Xolve Mar 02 '19 at 21:26
  • As posted by @Stargateur the sniappet clearly explains how lexical lifetimes can help to understand use of each variable clearly. Non Lexical Lifetimes (NLL) does help to write code easier but the scoping here seems a better idea and more idiomatic IMHO. – Xolve Mar 02 '19 at 21:29

1 Answers1

2

HashMap::get() returns Option<&V>, which you unwrap, getting &V, so you end up with prev being an immutable reference that points inside map. That's why you have an immutable borrow of map. The type of prev is &i32.

To solve your problem, you can dereference that reference:

let prev = *map.get(&(x - 1)).unwrap();

In this case, the type of prev is i32, and it is just a value on the stack. It does not point inside map => you don't borrow map immutably so you can borrow map mutably in the next line.

Your original code works just fine if you enable Rust 2018 edition, because of non-lexical lifetimes support.

Note that HashMap is not sorted, so the output lines are produced in arbitrary order. If you need sorting, you can consider using BTreeMap.

If you don't want to use either Copy (via *) or Clone at all, and don't want to switch to Rust 2018, you can do the following:

let value_to_insert = {
    let prev = map.get(&(x - 1)).unwrap();
    (prev + 1) * 10
}
map.insert(x, value_to_insert)

This is pretty much the same thing non-lexical lifetimes would do for you automatically in Rust 2018.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
frp
  • 1,119
  • 1
  • 10
  • 30
  • Updated. IMHO this is pointless for the values of i32 though. – frp Mar 02 '19 at 11:40
  • De-referencing with `*` operator is same as `clone()` which doesn't answer the question. Code snippet posted by @Stargateur actually solves it. – Xolve Mar 02 '19 at 21:28