Repository: rust
Issue: confusion when intersecting multiple HashSets
Labels: A-diagnostics,T-compiler


Context: I'm about a month in to learning Rust, doing Advent of Code to help learn. Day three this year has a problem where part of the solution is a nice fit for intersecting three HashSets. [Here is the code I ended up with](https://github.com/briandorsey/AdventOfCode/blob/58717699b8d3380f6deb2889d5f204168a7cd263/2022/day03/src/main.rs#L62).

I've extracted a minimal version of the error I got stuck at. I spent... an embarrassingly long time trying to get something working with `.intersection()`. This is what I expected to work based on understanding sets from other languages and reading the HashSet docs (and seeing lots of chained function calls in Rust). 

[playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=69e54c612f881becd9f8ae38eb8416f6)
```rust
use std::collections::HashSet;

fn main() {
    let a: HashSet<_> = "vJrwpWtwJgWrhcsFMMfFFhFp".chars().collect();
    let b: HashSet<_> = "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL".chars().collect();
    let c: HashSet<_> = "PmmdzqPrVvPwwTWBwg".chars().collect();

    let in_all = a
        .intersection(&b)
        .collect::<HashSet<_>>()
        .intersection(&c)
        .collect::<HashSet<_>>();

    println!("{in_all:?}");
}
```

The current output is:
(all output is from 1.65.0 stable)
```
error[[E0308]](https://doc.rust-lang.org/stable/error-index.html#E0308): mismatched types
  --> src/main.rs:11:23
   |
11 |         .intersection(&c)
   |          ------------ ^^ expected `&char`, found `char`
   |          |
   |          arguments to this function are incorrect
   |
   = note: expected reference `&HashSet<&char>`
              found reference `&HashSet<char>`
note: associated function defined here

For more information about this error, try `rustc --explain E0308`.
```

I didn't understand how to easily affect the char references inside the HashSets.

I eventually ended up here, which works... but I knew wasn't right, and wouldn't work well for a larger number of sets. 

```rust
use std::collections::HashSet;

fn main() {
    let a: HashSet<_> = "vJrwpWtwJgWrhcsFMMfFFhFp".chars().collect();
    let b: HashSet<_> = "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL".chars().collect();
    let c: HashSet<_> = "PmmdzqPrVvPwwTWBwg".chars().collect();

    let tmp1: HashSet<&char> = a.intersection(&b).collect();
    let tmp2: HashSet<&char> = b.intersection(&c).collect();
    let in_all: HashSet<&&char> = tmp1.intersection(&tmp2).collect();

    println!("{in_all:?}");
}
```

I don't have specific proposed output, but I would have loved hints that lead me to one of the two solutions below.

I looked at some other folks solutions to the same problem [notes and links here](https://github.com/briandorsey/AdventOfCode/blob/main/2022/day03/README.md) and with the caveat that I'm early in my Rust learning, here are the two outcomes I liked best: 

## Solution A
The solution I think I like best involves using operator sugar: HashSet [implements `&`](https://doc.rust-lang.org/src/std/collections/hash/set.rs.html#1156), which is clean and nearly what I expected intersection to work like. It's still awkward to give the extra &() reference in order to make the third intersection work. But, I'm probably missing some nice Rsut syntax, and I imagine there is a nice way to do this for the general case of a sequence of iteration. Maybe `.fold()` over an iterator or something?

[playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=5ef8c796d69a609cc821a2c1c3ee5a8a)
```rust
use std::collections::HashSet;

fn main() {
    let a: HashSet<_> = "vJrwpWtwJgWrhcsFMMfFFhFp".chars().collect();
    let b: HashSet<_> = "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL".chars().collect();
    let c: HashSet<_> = "PmmdzqPrVvPwwTWBwg".chars().collect();

    let in_all = &(&a & &b) & &c;

    println!("{in_all:?}");
}
```

output:
```
{'r'}
```


## Solution B

Looking at other solutions, lots of folks used `.copied()`... and just adding that almost works... and the error message guides me to a working solution. (The message when adding `.cloned()` is similarly good)

[playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=8e75291ccf10248c8953c957be7658fd)
```rust
use std::collections::HashSet;

fn main() {
    let a: HashSet<_> = "vJrwpWtwJgWrhcsFMMfFFhFp".chars().collect();
    let b: HashSet<_> = "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL".chars().collect();
    let c: HashSet<_> = "PmmdzqPrVvPwwTWBwg".chars().collect();

        let in_all = a
            .intersection(&b)
            .copied()
            .collect::<HashSet<_>>()
            .intersection(&c)
            .collect::<HashSet<_>>();

    println!("{in_all:?}");
}
```

output:
```
error[[E0716]](https://doc.rust-lang.org/stable/error-index.html#E0716): temporary value dropped while borrowed
  --> src/main.rs:8:22
   |
8  |           let in_all = a
   |  ______________________^
9  | |             .intersection(&b)
10 | |             .copied()
11 | |             .collect::<HashSet<_>>()
   | |____________________________________^ creates a temporary which is freed while still in use
12 |               .intersection(&c)
13 |               .collect::<HashSet<_>>();
   |                                       - temporary value is freed at the end of this statement
14 |
15 |       println!("{in_all:?}");
   |                  ------ borrow later used here
   |
help: consider using a `let` binding to create a longer lived value
   |
8  ~         let binding = a
9  +             .intersection(&b)
10 +             .copied()
11 +             .collect::<HashSet<_>>();
12 ~         let in_all = binding
   |

For more information about this error, try `rustc --explain E0716`.
```

And that works!:

[playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=ab06d4b965b81b554ac65e5bb00e6ca5)
```rust
use std::collections::HashSet;

fn main() {
    let a: HashSet<_> = "vJrwpWtwJgWrhcsFMMfFFhFp".chars().collect();
    let b: HashSet<_> = "jqHRNqRjqzjGDLGLrsFMfFZSrLrFZsSL".chars().collect();
    let c: HashSet<_> = "PmmdzqPrVvPwwTWBwg".chars().collect();

    let binding = a.intersection(&b).copied().collect::<HashSet<_>>();
    let in_all = binding.intersection(&c).collect::<HashSet<_>>();

    println!("{in_all:?}");
}
```

output:
```
{'r'}
```