The author has a follow up article[1] on their blog about it. They rewrite the rust code to eliminate the extra allocations they identified at the end of this post. It then performs 8 times faster than the javascript version.
However, I think they could have simply iterated on the code from the first post:
There are a lot of allocations because two strings are created every time they are pushed to the queue (.to_string()):
queue.push(candidate.to_string());
queue.push(token.to_string());
This is unnecessary, because candidate and token are both a single character.Rust has a char type that is useful to store single characters. Especially, unlike String which is heap-allocated, char is a primitive value that resides on the stack and CPU registers. Since we are comparing single characters, it's much better to use char.
So if the queue is defined as a Vec<char> instead of a Vec<String>, and the comparison function took as argument char rather than &String, it will spare a lot of allocations and this will speed up the final application.
let mut queue: Vec<char>
// ...
if !compare(candidate, token) {
queue.push(candidate);
queue.push(token);
// ...
fn compare(token1: char, token2: char) -> bool {
This has also the advantage of removing the references, making the code cleaner. Using Vec::with_capacity(n) instead of Vec::new() will also help lowering the allocation load by preemptively allocating a large enough memory slice. With Vec::new(), the vector will reallocate a few times.