7

I have a function that is supposed to pick random words from a list of words:

pub fn random_words<'a, I, R>(rng: &mut R, n: usize, words: I) -> Vec<&'a str>
where
    I: IntoIterator<Item = &'a str>,
    R: rand::Rng,
{
    rand::sample(rng, words.into_iter(), n)
}

Presumably that's a reasonable signature: Since I don't actually need the string itself in the function, working on references is more efficient than taking a full String.

How do I elegantly and efficiently pass a Vec<String> with words that my program reads from a file to this function? I got as far as this:

extern crate rand;

fn main() {
    let mut rng = rand::thread_rng();
    let wordlist: Vec<String> = vec!["a".to_string(), "b".to_string()];

    let words = random_words(&mut rng, 4, wordlist.iter().map(|s| s.as_ref()));
}

Is that the proper way? Can I write this without explicitly mapping over the list of words to get a reference?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • *working on references is more efficient than taking a full `String`* maybe, maybe not; it's really more about how you use the strings. If you convert the `&str` into a `String` later and throw away the `Vec`, then it would be better to just sample the `String`s directly. If you did need references to the items, I'd probably write the code similar to yours: `rand::sample(rng, words.iter().map(AsRef::as_ref), 4)`. – Shepmaster Feb 25 '16 at 13:44
  • 1
    IMHO your `random_words` doesn't do anything useful, you should use `rand::sample` directly. – starblue Feb 25 '16 at 14:16

1 Answers1

10

You can change your generic function to take anything that can be turned into a &str instead of having it take an iterator that yields a &str:

pub fn random_words<'a, I, R, J>(rng: &mut R, n: usize, words: I) -> Vec<&'a str>
where
    I: IntoIterator<Item = &'a J>,
    J: AsRef<str> + 'a,
    R: rand::Rng,
{
    rand::sample(rng, words.into_iter().map(AsRef::as_ref), n)
}
let words: Vec<&str> = random_words(&mut rng, 4, &wordlist);

The Book even has an entire chapter devoted to this topic

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
oli_obk
  • 28,729
  • 6
  • 82
  • 98
  • How would the implementation look like then? I tried various things but I can't get any variant of the body in my question past the borrow checker. –  Feb 25 '16 at 14:54
  • have you tried something like `rand::sample(rng, words.map(AsRef::as_ref), n)` ? – oli_obk Feb 25 '16 at 15:06
  • That's a type error, unfortunately. I'm sorry, but I'm pretty new to Rust, and need a lot of hand-holding still. –  Feb 25 '16 at 15:10
  • Yea, the problem was that `Item` needs to be reference, too. I updated the code together with the `rand::sample` call – oli_obk Feb 25 '16 at 15:40
  • Thank you very much! It builds, now give me two days to understand what's going on ;) –  Feb 25 '16 at 15:57