0

I am trying insert pairs of a variable into a HashSet, after checking if the pair is present already. I then need to do downstream work with the pair, within the same context. Here is a playground reproducing my problem:

https://play.rust-lang.org/?gist=5eecfa9c1e54b468d79955aab916dd84&version=stable&mode=debug&edition=2015

use std::collections::HashSet;

fn main() {
    let mut h = HashSet::new();
    let a = 1;
    let b = 2;
    if h.contains(&(&a, &b)) {
        println!("fail");
    }
    h.insert(&(&a, &b));
}
error[E0597]: borrowed value does not live long enough
  --> src/main.rs:10:15
   |
10 |     h.insert(&(&a, &b));
   |               ^^^^^^^^ - temporary value dropped here while still borrowed
   |               |
   |               temporary value does not live long enough
11 | }
   | - temporary value needs to live until here
   |
   = note: consider using a `let` binding to increase its lifetime

error[E0597]: `a` does not live long enough
  --> src/main.rs:10:17
   |
10 |     h.insert(&(&a, &b));
   |                 ^ borrowed value does not live long enough
11 | }
   | - `a` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

error[E0597]: `b` does not live long enough
  --> src/main.rs:10:21
   |
10 |     h.insert(&(&a, &b));
   |                     ^ borrowed value does not live long enough
11 | }
   | - `b` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

How can I check if a and b are in the set, then insert them afterwards if they are not, and then do other things with them? How are they being borrowed if I am loading in a reference to them within the same scope?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Ian Fiddes
  • 2,821
  • 5
  • 29
  • 49
  • 1
    Are your values actually integers? If so, it seems like you'd be better served by not using references to begin with. e.g. https://play.rust-lang.org/?gist=9e6af44a5bc1c335664c779a4b1586ca&version=stable&mode=debug&edition=2015 – loganfsmyth Sep 12 '18 at 00:31
  • *How are they being borrowed if I am loading in a reference to them* — that's what borrowing **is**: taking a reference to a value. – Shepmaster Sep 12 '18 at 00:40
  • Perhaps you could [edit] your question to expand on what part of the error message you are having trouble with? "values in a scope are dropped in the opposite order they are created" feels clear to me. – Shepmaster Sep 12 '18 at 00:42
  • 1
    I believe your question is answered by the answers of [How do I add references to a vector when the borrowed values are created after the vector?](https://stackoverflow.com/q/44987555/155423). If you disagree, please [edit] your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Sep 12 '18 at 00:42
  • 1
    The combination of `h.contains(&(&a, &b))` and `h.insert(&(&a, &b))` is suspicious. Normally you would call `insert` with a `T` and `contains` with a reference-to-`T`. It still works the way you wrote it (well, it would, if you follow the answer to the question Shepmaster linked) but the compiler infers that you want a `HashSet<&(&i32, &i32)>` when you probably wanted a `HashSet<(&i32, &i32)>`. (Or if loganfsmyth is correct, you really want `HashSet<(i32, i32)>` and no references at all.) – trent Sep 12 '18 at 03:22

1 Answers1

2

Leaving "why"s aside for a moment, the example as presented has three issues:

  1. The tuple &(...) ceases to exist once the function call ends. So inserting a reference to something that will not exist once HashSet::insert returns is an error, and one that the compiler thankfully catches.

  2. You declared the HashSet before the variables you want to reference, which the compiler sees as an error (likely due to the fact that, at some point in the HashSet's scope, the variables a and b do not exist). This is something I've been burnt by on numerous occasions and is a limitation you should keep in mind until the compiler improves.

  3. As pointed out in the comments above, HashSets take T when inserting and &T when accessing. Your code uses T for both.

The following code fixes these issues:

use std::collections::HashSet;

fn main() {
    let a = 1;
    let b = 2;
    let mut h = HashSet::new();
    if h.contains(&(&a, &b)) {
        println!("fail");
    }
    h.insert((&a, &b));
}

playground

Compare the lines that declare the HashSet and insert the tuple.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Tenders McChiken
  • 1,216
  • 13
  • 21
  • @Shepmaster [With NLLs, the compiler can shorten the lifetime of `h` so that it doesn't outlive `a` and `b`.](https://play.rust-lang.org/?gist=353742ca6fc1b8d4fcf63eb6af92ff9f&version=nightly&mode=debug&edition=2018) – trent Sep 12 '18 at 16:02
  • @trentcl hmm. I'll investigate more into the why on that and update the linked question. Thanks for pointing that out! I'll remove my incorrect comment. – Shepmaster Sep 12 '18 at 16:25
  • @trentcl I updated the linked question. Pedantically, `h` still outlives `a` and `b` (and it always "could"). Compiler nuances are tough. – Shepmaster Sep 12 '18 at 19:03