18

I'm trying to convert a HashSet<String> into a sorted vector that can then be joined with commas:

use std::collections::HashSet;

fn main() {
    let mut hs = HashSet::<String>::new();
    hs.insert(String::from("fee"));
    hs.insert(String::from("fie"));
    hs.insert(String::from("foo"));
    hs.insert(String::from("fum"));

    let mut v: Vec<&String> = hs.iter().collect();
    v.sort();

    println!("{}", v.join(", "));
}

This will not compile:

error[E0599]: no method named `join` found for struct `std::vec::Vec<&std::string::String>` in the current scope
  --> src/main.rs:13:22
   |
13 |     println!("{}", v.join(", "));
   |                      ^^^^ method not found in `std::vec::Vec<&std::string::String>`

I understand why I can't join the Vec<&String>, but how can I convert the HashSet to a Vec<String> instead, so it can be joined?

The examples given in What's an idiomatic way to print an iterator separated by spaces in Rust? do not seem to apply because the iterator for Args returns String values, unlike the iterator for HashSet which returns &String.

Shepmaster
  • 326,504
  • 69
  • 892
  • 1,159
Ralph
  • 30,462
  • 36
  • 132
  • 269
  • 1
    Does this answer your question? [What's an idiomatic way to print an iterator separated by spaces in Rust?](https://stackoverflow.com/questions/36941851/whats-an-idiomatic-way-to-print-an-iterator-separated-by-spaces-in-rust) – E_net4 - Krabbe mit Hüten Mar 27 '20 at 19:45
  • 1
    You could call `into_iter` to take ownership of the values. – squiguy Mar 27 '20 at 19:50
  • *the iterator for `HashSet` which returns `&String`* — one of the many iterators of `HashSet` does that, yes. Others do not. You also can format a `&String` just fine. – Shepmaster Mar 27 '20 at 20:17

2 Answers2

24

I encourage you to re-read The Rust Programming Language, specifically the chapter on iterators. Next, become familiar with the methods of Iterator.

The normal way I'd expect to see this implemented is to convert the HashSet to an iterator and then collect the iterator to a Vec:

let mut v: Vec<_> = hs.into_iter().collect();

In this case, I'd prefer to use FromIterator directly (the same trait that powers collect):

let mut v = Vec::from_iter(hs);

Focusing on your larger problem, use a BTreeSet instead, coupled with What's an idiomatic way to print an iterator separated by spaces in Rust?

use itertools::Itertools; // 0.10.1
use std::collections::BTreeSet;

fn main() {
    // Create the set somehow
    let hs: BTreeSet<_> = ["fee", "fie", "foo", "fum"]
        .into_iter()
        .map(String::from)
        .collect();

    println!("{}", hs.iter().format(", "));
}
Shepmaster
  • 326,504
  • 69
  • 892
  • 1,159
  • I'm part-way through my first reading of _The Rust Programming Language_. I figured out how to make it work by changing to `HashSet`. After that change, the `join` worked. In my case, using `&'static str` is fine since the HashSet contains only string literals. – Ralph Mar 27 '20 at 21:21
0

There's a straightforward way to convert a HashSet of Strings into a Vec of Strings using the turbofish (::<>):

let result_vec: Vec<String> = result_set.into_iter().collect::<Vec<String>>();
Shepmaster
  • 326,504
  • 69
  • 892
  • 1,159
  • 1
    Specifying the type on both the variable _and_ via the turbofish is redundant. Specifying the type inside the collection is also redundant. I'd write it as `let result_vec = result_set.into_iter().collect::>()`. – Shepmaster Nov 24 '21 at 16:37