In this exercise we will implement a Rust-y, simpler variant of Pig Latin: Depending on if a word starts with a vowel or not, either a suffix or a prefix is added to the word
You will learn how to
- create a Rust library
- split a
&str
at specifiedchar
- get single
char
out of a&str
- iterate over a
&str
- define Globals
- compare a value to the content of an array
- use the Rust compiler’s type inference to your advantage
- to concatenate
&str
- return the content of a
Vec<String>
asString
.
- define variables as mutable
- use for loop
- use an if/else construction
- read Rust documentation
- define a function with signature and return type
- define arrays and vectors
- distinguish between
String
and&str
- the Vowels of English alphabet →
['a', 'e', 'i', 'o', 'u']
- a sentence is a collection of Unicode characters with words that are separated by a space character (
U+0020
)
✅ Implement a function that splits a sentence into its words, and adds a suffix or prefix to them according to the following rules:
-
If the word begins with a vowel add prefix “sr” to the word.
-
If the word does not begin with a vowel add suffix “
rs
” to the word.
For example, the sentence Implement a function that splits a sentence into its words
becomes srImplement sra functionrs thatrs splitsrs sra sentencers srinto srits wordsre
.
The function returns a String
containing the modified words.
Find the exercise template in ../../exercise-templates/rustlatin
The folder contains each step as its own numbered project, containing a lib.rs
file. Each lib.rs
contains starter code and a test that needs to pass in order for the step to be considered complete.
Complete solutions are available ../../exercise-solutions/rustlatin
A part of this exercise is seeing type inference in action and using it to help to determine the type the function is going to return. To make sure the file can be indexed by Rust Analyzer, make sure you open the relevant step by itself - e.g. exercise-templates/rustlatin/step1
. You can close each step when complete and open the next one.
✅ Iterate over the sentence to split it into words. Use the white space as separator. This can be done with the .split()
method, where the separator character ' '
goes into the parenthesis. This method returns an iterator over substrings of the string slice. In Rust, iterators are lazy, that means just calling .split()
on a &str
doesn’t do anything by itself. It needs to be in combination with something that advances the iteration, such as a for
loop, or a manual advancement such as the .next()
method. These will yield the actual object you want to use. Push each word into the vector collection_of_words
. Add the correct return type to the function signature.
✅ Run the test to see if it passes.
Solution
fn rustlatin(sentence: &str) -> Vec<String> {
let mut collection_of_words = Vec::new();
for word in sentence.split(' ') {
collection_of_words.push(word.to_string())
}
collection_of_words
}
✅ After iterating over the sentence to split it into words, add the suffix "rs"
to each word before pushing it to the vector.
✅ To concatenate two &str
the first needs to be turned into the owned type with .to_owned()
. Then String
and &str
can be added using +
.
✅ Add the correct return type to the function signature.
✅ Run the test to see if it passes.
Solution
fn rustlatin(sentence: &str) -> Vec<String> {
let mut collection_of_words = Vec::new();
for word in sentence.split(' ') {
collection_of_words.push(word.to_owned() + "rs")
};
collection_of_words
}
✅ After iterating over the sentence to split it into words, add the first character of each word to the vector.
✅ Check the Rust documentation on the primitive str Type for a method that returns an iterator over the chars
of a &str
. The char
type holds a Unicode Scalar Value that represents a single character (although just be aware the definition of character is complex when talking about emojis and other non-English text).
Since iterators don’t do anything by themselves, it needs to be advanced first, with the .next()
method. This method returns an Option(Self::Item)
, where Self::Item
is the char
in this case. You don’t need to handle it with pattern matching in this case, a simple unwrap()
will do, as a None
is not expected to happen.
✅ Add the correct return type to the function signature. Run the test to see if it passes.
Solution
fn rustlatin(sentence: &str) -> Vec<char> {
let mut collection_of_chars = Vec::new();
for word in sentence.split(' ') {
let first_char = word.chars().next().unwrap();
collection_of_chars.push(first_char);
};
collection_of_chars
}
Step 4: Putting everything together: Comparing values and returning the content of the vector as String
.
✅ Add another function that checks if the first character of each word is a vowel. contains() is the method to help you with this. It adds the prefix or suffix to the word according to the rules above.
Call the function in each iteration.
In fn rustlatin
return the content of the vector as String
. Run the tests to see if they pass.
Solution
const VOWELS: [char; 5] = ['a', 'e', 'i', 'o', 'u'];
fn latinize(word: &str) -> String {
let first_char_of_word = word.chars().next().unwrap();
if VOWELS.contains(&first_char_of_word) {
"sr".to_string() + word
} else {
word.to_string() + "rs"
}
}
If not already done, use functional techniques (i.e. methods on iterators) to write the same function. Test this new function as well.
Solution
const VOWELS: [char; 5] = ['a', 'e', 'i', 'o', 'u'];
fn rustlatin_match(sentence: &str) -> String {
// transform incoming words vector to rustlatined outgoing
let new_words: Vec<_> = sentence
.split(' ')
.into_iter()
.map(|word| {
let first_char_of_word = word.chars().next().unwrap();
if VOWELS.contains(&first_char_of_word) {
"sr".to_string() + word
} else {
word.to_string() + "rs"
}
})
.collect();
new_words.join(" ")
}