Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get a random character from a string and append to another string

Tags:

string

rust

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());
like image 475
Quintus Avatar asked Mar 02 '26 20:03

Quintus


1 Answers

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)

like image 165
Lukas Kalbertodt Avatar answered Mar 05 '26 21:03

Lukas Kalbertodt