Code Refactoring of Bob - Exercism in Rust
Table of Contents
Introduction
This comes from the side exercise of exercism in rust.
Mentor gaetanww
helped me a lot on code refactoring and logical improvements advice with great patience and careful instruction. He even changed my attitude towards work and researching. I have nothing else to say to express my thanks to my mentor.
So I decide to record the whole process of code improvements and advice from gaetanww
.
I am pretty sure that you could gain lessons from this post as well.
Exercise Introduction:
Instructions
Bob is a lackadaisical teenager. In conversation, his responses are very limited.
Bob answers ‘Sure.’ if you ask him a question, such as “How are you?”.
He answers ‘Whoa, chill out!’ if you YELL AT HIM (in all capitals).
He answers ‘Calm down, I know what I’m doing!’ if you yell a question at him.
He says ‘Fine. Be that way!’ if you address him without actually saying anything.
He answers ‘Whatever.’ to anything else.
Bob’s conversational partner is a purist when it comes to written communication and always follows normal rules regarding sentence punctuation in English.
Test suite
use bob;
Solution
Version 1
Comments on Version 1
Check out using
match
instead ofif
. For some things, like destructuring, it’s required. It’s an idiom specific to Rust, so I always default to it unless the logic is comparing many different things and a match, in that case, makes it messy.Have a look at
.ends_with()
to check if it’s a question, it expresses your intent better.at line 48 you wrote:
if f != Some && f != Some && f != Some && f != Some
The method
.trim()
gets rid of all these characters for you :)We don’t necessarily need to create separate functions here. We can put this all in one by setting these calls to variables instead (check iterator methods, like
.any()
etc.)Your function
is_all_capitalized()
does not handle non-ASCII characters.
Version 2
Comments on Version2
One of the main goals of exercism is to teach ‘fluency’ in a programming language. One of the aspects of fluency in Rust is finding information easily in the standard library and third-party libraries, there is often a way to do things more shortly, correctly and efficiently.
For example, you can replace some of your functions by methods on characters (
.is_alphabetic()
,.is_uppercase
) or iterators and strings (.is_empty()
instead ofstring.len() == 0
). May I suggest you look up these functions and see if it would help you? It might also bring some edge-cases that you haven’t considered (IsÜ
uppercase and/or alphabetic? ).- My answer:
Ü
is uppercase and is not alphabetic.
- My answer:
Version 3
Comments on Version 3
You recompute
message.trim()
several times (at line 2, 12, 16 and 18), it could be better to store it in a variable like:let trimmed_message = message.trim; or even let message = message.trim;
This is probably going to get optimized by the compiler, but it would at least make your program a bit shorter.
In the functions
is_all_capitalized
andhave_letters
you use an iterator (message.chars()
) match on the elements to verify some properties on them. You can rewrite those two functions with only iterators methods, would you like to try it?It would look a bit like that:
let is_all_capitalized = message.chars.method1;
If you’re stuck, have a look at the any() and all() methods.
Version 4
Comments on Version4
You can use
match
statements on tuples. For example:match
Can you use that to make your code more straightforward?
Version 5
Comments on Version 5
We don’t tend to use lots of return statements, so if you have a lot of them you might be able to iprove your code. The reason why wer don’t use them so much is that it makes the control flow of your program more difficult to read. For example, in that exercise, you can get rid of all the return statements and have only one big match. Try and find how to do it :)
One way you can move forward with this exercise is to store boolean results in variable: for example:
let is_empty = message.is_empty; let is_question = message.ends_with; let is_all_capitalized = etc...
Hopefully this will clear things up for the
match
statement.
Version 6
Comments on Version 6
- Rust has other paradigms that are fun and interesting to explore. One of them is that Rust is an expression-oriented language where all statements return a value, and that’s why you don’t necessarily need a
return
to return a value from a function. You’ll see that if you avoid using it (when it makes sense to, returning early is a valid use case for usingreturn
) it will compose very well with other bits of rust code.
As for your code, if you want to get rid of te return statements, try:
match
I think you can simplify the problem to have two or three variables in the match statements.
Consider matching with:
let is_yelled = is_all_capitalized && is_having_letters; match
Or even:
if message.is_empty match
Your code can be refactored. Please see:
if message.is_empty
match
Version 7
Comments on Version 7
- Well done!