0

I am writing a service that will collect a great number of values and build large structures around them. For some of these, lookup tables are needed, and due to memory constraints I do not want to copy the key or value passed to the HashMap. However, using references gets me into trouble with the borrow checker (see example below). What is the preferred way of working with run-time created instances?

use std::collections::HashMap;

#[derive(PartialEq, Eq, PartialOrd, Ord, Hash)]
struct LargeKey;
struct LargeValue;

fn main() {
    let mut lots_of_lookups: HashMap<&LargeKey, &LargeValue> = HashMap::new();
    let run_time_created_key = LargeKey;
    let run_time_created_value = LargeValue;
    lots_of_lookups.insert(&run_time_created_key, &run_time_created_value);
    lots_of_lookups.clear();
}

I was expecting clear() to release the borrows, but even if it actually does so, perhaps the compiler cannot figure that out?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Lars Rönnbäck
  • 788
  • 1
  • 5
  • 11
  • [Borrow checker doesn't realize that `clear` drops reference to local variable](https://stackoverflow.com/q/39946430/155423) – Shepmaster Jul 08 '17 at 15:23

1 Answers1

2

Also, I was expecting clear() to release the borrows, but even if it actually does so, perhaps the compiler cannot figure that out?

At the moment, borrowing is purely scope based. Only a method which consumes the borrower can revoke the borrow, which is not always ideal.

What is the preferred way of working with shared run-time created instances?

The simplest way to expressed shared ownership is to use shared ownership. It does come with some syntactic overhead, however it would greatly simplify reasoning.

In Rust, there are two simple standard ways of expressing shared ownership:

  • Rc<RefCell<T>>, for sharing within a thread,
  • Arc<Mutex<T>>, for sharing across threads.

There are some variations (using Cell instead of RefCell or RWLock instead of Mutex), however those are the basics.

Beyond syntactic overhead, there's also some amount of run-time overhead going into (a) increasing/decreasing the reference count whenever you make a clone and (b) checking/marking/clearing the usage flag when accessing the wrapped instance of T.

There is one non-negligible downside to this approach, though. The borrowing rules are now checked at runtime instead of compile time, and therefore violations lead to panic instead of compile time errors.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722