RUST: How can I optimize this password cracking algorithm?
First, I would change the approach fundamentally: I'd consider the plaintext as a 17-digit number in &quot;base 11&quot; (because there are 11 &quot;letters&quot; in the alphabeth). Then the loop becomes a simple iteration through a range from 0 to 1117-1. On top of that, threading will be easy to add using rayon.
As you were told, we should re-use memory for strings and not generate the wordlist in advance.
To re-use memory for strings, we treat them as long-living containers and call .clear() whenever we need to reuse them for another purpose.
This part in your code
for letter in alphabet.iter() {
    let new_word = format!(&quot;{}{}&quot;, word, letter);

    temp.push(new_word);
}

is very inefficient as it's calling format!, which is complex machinery, just to append a letter. It would be better to have at least String::push there, but we will take another approach below.

On 4 cores, the all-in-one is roughly as fast as just your non-parallelized first pass. For parallelism, I did parallel fold after Range&lt;u64&gt;. So each thread has a little bit of its own state there.
Make sure you add rayon = &quot;0.6&quot; in the Cargo manifest.
Here is the code:
#[cfg(debug_assertions)]
use num_format::{Locale, ToFormattedString};
use std::str;
use std::io::Write;
use rayon::prelude::*;

static PASS_LENGTH: u32 = 17;

#[derive(Default)]
struct State {
    plaintext: Vec&lt;u8&gt;,
    intermediate: Vec&lt;u8&gt;,
}

impl State {
    fn new() -&gt; Self {
        let mut result = State::default();
        for _ in 0 .. PASS_LENGTH {
            result.plaintext.push(b&quot;x&quot;[0]);
        }
        result.plaintext.extend(b&quot;........................................................!1&quot;.iter().copied());
        result
    }
}

fn main() {
    let goal_hash = &quot;dbba1bfe930d953cabcc03d7b6ab05e&quot;;

    let num_of_md5 = 4;
    let alphabet = &quot;bdeilmostu-&quot;;
    let alphabet_len = alphabet.len() as u64;

    let total = alphabet_len.pow(PASS_LENGTH);

    (0 .. total).into_par_iter().fold(
        || State::new(),
        |mut state, num| {
            for i in 0 .. PASS_LENGTH {
                let letter = ((num / alphabet_len.pow(i)) % alphabet_len) as usize;
                state.plaintext[i as usize] = alphabet.as_bytes()[letter];
            }

            state.intermediate.clear();
            write!(&amp;mut state.intermediate, &quot;{:x}&quot;, md5::compute(&amp;state.plaintext[..]));
            for _ in 1 .. num_of_md5 {
                let digest = md5::compute(&amp;state.intermediate[..]);
                state.intermediate.clear();
                write!(&amp;mut state.intermediate, &quot;{:x}&quot;, digest);
            }
            let result = &amp;state.intermediate[..];

            if result == goal_hash.as_bytes() {
                println!(&quot;We found the password: {}&quot;, str::from_utf8(&amp;state.plaintext[..]).unwrap());
            }

            if num % 1_000_000 == 0 &amp;&amp; num != 0 {
                #[cfg(debug_assertions)]
                println!(
                    &quot;Attempts: {}/{}, Text: {}&quot;,
                    num, total, str::from_utf8(&amp;state.plaintext[..]).unwrap()
                );
            }

            state
        }
    ).for_each(|_| {});
}
```