Get a random character from a string and append to

2019-02-28 14:54发布

I'm trying to write the Rust equivalent of the following C++ code:

result += consonants[rand() % consonants.length()];

It is meant to take a random character from the string consonants and append it to the string result.

I seem to have found a working Rust equivalent, but it's... monstrous, to say the least. What would be a more idiomatic equivalent?

format!("{}{}", result, consonants.chars().nth(rand::thread_rng().gen_range(1, consonants.chars().count())).unwrap().to_string());

标签: string rust
1条回答
放我归山
2楼-- · 2019-02-28 15:09

A few things:

  • You don't need to use format!() here. There is String::push() which appends a single char.

  • There is also the rand::sample() function which can randomly choose multiple elements from an iterator. This looks like the perfect fit!

So let's see how this fits together! I created three different versions for different use cases.

1. Unicode string (the general case)

let consonants = "bcdfghjklmnpqrstvwxyz";
let mut result = String::new();

result.push(rand::sample(&mut rand::thread_rng(), consonants.chars(), 1)[0]);
//                                                                    |  |
//                             sample one element from the iterator --+  |
//                                                                       |
//                      get the first element from the returned vector --+

(Playground)

We sample only one element from the iterator and immediately push it to the string. Still not as short as with C's rand(), but please note that rand() is considered harmful for any kind of serious use! Using C++'s <random> header is a lot better, but will require a little bit more code, too. Additionally, your C version can't handle multi-byte characters (e.g. UTF-8 encoding), while the Rust version has full UTF-8 support.

2. ASCII string

However, if you only want to have a string with English consonants, then UTF-8 is not needed and we can make use of O(1) indexing, by using a byte slice:

use rand::{thread_rng, Rng};

let consonants = b"bcdfghjklmnpqrstvwxyz";
let mut result = String::new();

result.push(thread_rng().choose(consonants).cloned().unwrap().into());
//      convert Option<&u8> into Option<u8> ^^^^^^   
// unwrap, because we know `consonants` is not empty ^^^^^^
//                                   convert `u8` into `char` ^^^^

(Playground)

3. Collection of characters with Unicode support

As mentioned in the comments, you probably just want a collection of characters ("consonants"). This means, we don't have to use a string, but rather an array of chars. So here is one last version which does have UTF-8 support and avoids O(n) indexing:

use rand::{thread_rng, Rng};

// If you need to avoid the heap allocation here, you can create a static
// array like this: let consonants = ['b', 'c', 'd', ...];
let consonants: Vec<_> = "bcdfghjklmnpqrstvwxyz".chars().collect();
let mut result = String::new();

result.push(*thread_rng().choose(&consonants).unwrap());

(Playground)

查看更多
登录 后发表回答