Problem overview
The function convert_to_hacker_speak takes a String called input and returns a new string in which some letters are replaced by digits:
abecomes4;ebecomes3;ibecomes1;obecomes0;sbecomes5.
Every other character, including spaces, stays as it is. For "javascript is cool" the result is "j4v45cr1pt 15 c00l": the two as, the s, the i and the two os are replaced.
Approach
Go through the input one character at a time and decide what to add to the output. For each character, there are six cases: the five special letters and everything else. A chain of if / else if branches covers them, and only the first branch whose condition is true runs.
The result is built in a separate new string. The loop reads from input and appends to the new string, which starts out empty and grows by one character per pass. The input is never edited in place. Rust strings are stored as UTF-8 bytes, and there is no way to assign to the character at a position, so building a new string is the natural approach.
Here is the word "coder" traced. The c matches no branch and is copied. The o becomes 0, the d is copied, the e becomes 3 and the r is copied. The output after the last pass is "c0d3r", which has the same length as the input, because each replacement swaps one character for one character.
The order of the branches does not matter for correctness here, since no character can match two of the conditions. What does matter is that every character reaches exactly one push.
Step by step
- Create the output string.
String::new()gives an empty, growable string. It needsmutso that characters can be appended. - Iterate over the characters. A
Stringcannot be looped over directly. Thechars()method returns an iterator that yields each character as achar, and the loop binds each one toc. - Handle
'a'. Whenc == 'a', push'4'.pushappends a singlechar. - Handle
'e'in anelse if, pushing'3'. Theelse ifis only tested when the conditions above it failed. - Handle
'i', pushing'1'. - Handle
'o', pushing'0'. - Handle
's', pushing'5'. - Keep everything else. The final
elsepushescunchanged, so spaces, digits, punctuation and other letters survive. - Return the result after the loop, when every character has been handled.
The solution in Rust
impl Solution {
fn convert_to_hacker_speak(input: String) -> String {
let mut solution = String::new();
for c in input.chars() {
if c == 'a' {
solution.push('4');
} else if c == 'e' {
solution.push('3');
} else if c == 'i' {
solution.push('1');
} else if c == 'o' {
solution.push('0');
} else if c == 's' {
solution.push('5');
} else {
solution.push(c);
}
}
return solution;
}
}
Some Rust details here:
charversus string. Single quotes make achar, such as'a', and double quotes make a string slice, such as"a". The comparisonc == 'a'needs acharon both sides.pushversuspush_str.pushappends onechar.push_strappends a whole&str. Writingsolution.push("4")does not compile.- Why
chars()and not indexing. Rust does not allowinput[i]on a string, because a byte position is not always a character position. Characters outside plain ASCII take more than one byte.chars()decodes the text correctly, so accented letters pass through untouched. - Ownership. The function takes
inputby value, so it owns the string. Iterating withchars()only borrows it. The returnedsolutionis moved out to the caller.
A shorter way to write the same chain
Rust has a match expression that fits a case list like this one. It picks a replacement character, and a single push follows:
let r = match c {
'a' => '4',
'e' => '3',
'i' => '1',
'o' => '0',
's' => '5',
other => other,
};
solution.push(r);
The last arm, other => other, catches every remaining character and passes it through. It behaves exactly like the if chain. The lesson uses the chain because if and else if are the tools of this chapter.
Complexity
Time: O(n), where n is the number of characters. The loop visits each one once, and each branch takes constant time. Appending is cheap on average, because the string's buffer grows geometrically.
Space: O(n). The new string holds one character for each character of the input.
Edge cases and common mistakes
- An empty string. The loop does not run, and the result is an empty string.
- Uppercase letters. Only lowercase letters are replaced.
"Apple Orange SEA"becomes"Appl3 Or4ng3 SEA": the capitalA,O,SandEare left alone, as the task describes. - Using separate
ifstatements instead ofelse if. A finalelsethen belongs only to the lastif, so every character that was already replaced by an earlier branch is also pushed a second time, unchanged. The chain needselse ifso that exactly one branch runs. - Forgetting the default case. Without the final
else, spaces and the other letters would be dropped from the output. - Returning inside the loop. The
return solution;belongs after the closing brace of the loop. - Non-ASCII text.
"café ñ"becomes"c4fé ñ". Characters that are not in the list are copied whole.
Testing
"javascript is cool"gives"j4v45cr1pt 15 c00l"."programming is fun"gives"pr0gr4mm1ng 15 fun"."become a coder"gives"b3c0m3 4 c0d3r".""gives"".
