Part 6. Iterating over string (10 points)
Strings in Rust are similar to vectors of characters except that, unfortunately, they’re slightly more difficult to use because text is inherently complicated than vectors.
The most obvious difficulty is you cannot access individual characters in the same way you’d access individual elements of a vector.
#![allow(unused)] fn main() { let x = vec![1, 2, 3]; assert_eq!(x[0], 1); let y = "ABC"; assert_eq!(y[0], 'A'); // FAILS! }
That last line fails (click the Run button to see the error) because you cannot index into a string like this. The problem is that Rust stores all strings in the UTF-8 encoding. This is a variable-length encoding where different characters may take a different number of characters to encode. So if you want to get the 10 th character from a string, for example, you cannot easily figure out where in the encoded data for the string the 10 th character starts without starting at the beginning of the string.
This means that to work with characters, we’re going to need to use an
iterator over the characters. Fortunately, the chars()
function returns an
iterator that returns each character.
#![allow(unused)] fn main() { let example = "Here is my string"; for ch in example.chars() { println!("{ch}"); } }
Just as we’ve made a distinction between Vec<i32>
and &[i32]
where the
latter is a reference to the former, in Rust we have a String
data
type and a reference to a string &str
. When we have a function that takes a
reference to a string, we use an argument of type &str
. Note that strings we
create by enclosing text in quotation marks have type &str
and not
String
. If we have a String
and we want to pass it to a function that
takes a &str
, we get a reference to the string using &
just as we did with
vectors and reference to vectors.
Your task
Write a function
#![allow(unused)] fn main() { fn clown_case(s: &str) -> String { todo!("Implement me") } }
that takes a &str
as input and returns a new String
that alternates
capitalization and includes a clown emoji, 🤡, at the beginning and end of the
string as long as s
is not the empty string. If s
is the empty string,
return just a single clown emoji.
Here are some examples. I recommend you turn them into unit tests before you
implement clown_case
.
""
->"🤡"
"I'm just asking questions"
->"🤡i'M jUsT aSkInG qUeStIoNs🤡"
"Μην είσαι κλόουν στα ελληνικά!"
->"🤡μΗν ΕίΣαΙ κΛόΟυΝ σΤα ΕλΛηΝιΚά!🤡"
- If
ch
is achar
, you can usech.is_alphabetic()
to decide if you want to lowercase or uppercase the letter or just include it in the result unchanged. I.e., only uppercase/lowercase the characters for whichch.is_alphabetic()
returns true. - If
ch
is achar
, thench.to_lowercase()
andch.to_uppercase()
return iterators tochar
s rather than achar
itself. Why? Well, in some languages converting the case changes the number of characters. E.g, a capitalß
isSS
. You can add the elements of an iterator to a string using theextend()
function.#![allow(unused)] fn main() { let mut result = String::new(); result.extend('ß'.to_uppercase()); assert_eq!(result, "SS"); }
- You can append a
char
to aString
by usingpush()
. You can append a&str
to aString
by usingpush_str()
.
At this point you’re done and can submit your code.
However, this is one more optional step you can take if you’d like: Comment
out the existing code in your main()
function. (In VS Code, you can comment
or uncomment multiple lines at once by selecting all of the lines and hitting
Ctrl-/.) Write some code in main()
to read a line of
input from stdin
, pass it to clown_case()
, and then print the results. Now
you can run the program and have it turn any input you enter into clown case.
(I have been reliably informed that this is a lot of fun to use.)