Make your Rust code more DRY

Pascal Precht
InstructorPascal Precht
Share this video with your friends

Social Share Links

Send Tweet
Published 4 years ago
Updated 3 years ago

This lesson takes an existing function with duplicated functionality and makes it more DRY by extracting core functionalities into another function.

Instructor: [00:00] To wrap it up, let's take this duplicated functionality of reading the input and parsing the value, and put it in a separate function. For that, we go ahead and create a function, readUserInput(), which will return a number. Then, we just take one of our input functionalities, copy it over, and make it a little bit more generic.

[00:30] We don't need this. Let's call this one input instead, change it here as well. Our variable is now going to be a digit. This is going to be input, this is going to be digit. Last but not least, we need to return the digit.

[00:49] A little bit of formatting, and then we can go ahead and make use of that function by saying, let a = readUserInput(). Then, we get rid of this. We can do exactly the same for our variable b, copy this over here, "Please enter the first number," and there we have our optimized program.

[01:20] This should work the same way as before, so if we do cargo run, we can enter a first number and a second number. It still works. It still keeps us asking. That's how you make your code a bit more dry.

J. Matthew
J. Matthew
~ 4 years ago

Interesting, so I see the return keyword can be implied at the end of a function (or block?), as demonstrated in read_user_input. I guess that's been true in the sum function as well.

I noticed you changed the Ok function from Ok(val) => { digit = val; } to Ok(val) => digit = val,; I also noticed that the trailing comma wasn't required in the first instance but appears to be in the second. What are the rules for this stuff? I'm guessing these are yet more signals to the compiler (and that it's similar to ES6 JavaScript in that you can dispense with the curly braces when using its fat arrow and only returning a single statement, e.g. const myFunc = (a, b) => a + b).

Thanks for the great course! Looking forward to more guidance on Rust.

Pascal Precht
Pascal Prechtinstructor
~ 4 years ago

Hey Matt,

Interesting, so I see the return keyword can be implied at the end of a function (or block?), as demonstrated in read_user_input. I guess that's been true in the sum function as well.

So the rule is:

Every line inside a function that ends with a ; is a statement and if it's the last line of a function body and it doesn't end with a ; it'll return the value of that line's expression.

So yes:

fn sum(a: i32, b: i32) -> i32 {
  a + b
}

^ Therefore returns whatever that last line in the function body evaluates to. However, it's still possible to use explicit return statements, in fact it's needed if you want to return earlier inside a function body.

similar to ES6 JavaScript in that you can dispense with the curly braces when using its fat arrow and only returning a single statement, e.g. const myFunc = (a, b) => a + b).

This is correct. A match branch that is a single-line statement/expression can be written as:

Ok(val) => { some_expr }

Or

Ok(val) => some_expr

Generally, I'd recommend to use as little symbols/characters to express the same thing as possible unless readability suffers from it.

Colin Maroney
Colin Maroney
~ 4 years ago

i'm curious as to why you can pass a and b to sum but it doesn't move them like the other function did, so that you're still able to pass a and b into println!() without them being & references.

Colin Maroney
Colin Maroney
~ 4 years ago

i'm also curious as to why you have to say "Pascal".to_string() when assigning the name....is "Pascal" by itself not a string already?

Pascal Precht
Pascal Prechtinstructor
~ 4 years ago

Hey Colin!

i'm curious as to why you can pass a and b to sum but it doesn't move them like the other function did, so that you're still able to pass a and b into println!() without them being & references.

Excellent question. So the reason there's actually no move happening is because... Rust will only not move values that are either a) primitives that don't require heap allocation (e.g. numbers, those just end up on the stack) or b) types that implement the Copy trait.

i'm also curious as to why you have to say "Pascal".to_string() when assigning the name....is "Pascal" by itself not a string already?

Not exactly. In Rust there can be values of type String, &String, &str and then there's also str, but the last one is usually only accessible via a reference (so &str).

I didn't want to go into that in this lesson because it'll require better understanding of how Rust manages memory as well as ownershiip.

I wrote an article to explain this for now: https://blog.thoughtram.io/string-vs-str-in-rust/

Aviral Kulshreshtha
Aviral Kulshreshtha
~ 3 years ago

@Colin thanks for these questions, these were the same questions I had. @Pascal, thanks a huge lot for answering in such detail and sharing the artical about strings.

Markdown supported.
Become a member to join the discussionEnroll Today