I'm doing this advent of code in rust to learn it (started today with the rust book too since the language is becoming more interesting to me) and I'm having some doubts as of how to comply with rust style.
Just read in the book that in rust is more idiomatic to use an expression at the end of a function than a return statement, so I've been going through these past days challenges and refactoring them for this but I have some doubts.
First the commits where I change it from returns to expressions:
If you look at day1.rs
method get_top_three
, I've modified it where I create a variable, and I assign it in an if, else if, else
but my initial idea was to not have the else
at all and have something like
if current > first {
(current, first, second);
} else if current > second {
top_three = (first, current, second);
} else if current > third {
top_three = (first, second, current);
}
(first, second, third)
would this be possible in some way and maybe better? I've gotten used to avoid having an else
expression and just returning the "default" result but maybe this is not the way in rust.
Besides this I'm still not sure when to use match
in place of if
, so if any of you look at my code and has some comments about my uses (or anything else to be honest) it would be greatly appreciated.
With a lot of missing context and guessing, I assume that your question is as follows.
You had the code:
fn get_top_three(current: i32, first: i32, second: i32, third: i32) -> (i32, i32, i32) {
if current > first {
return (current, first, second);
} else if current > second {
return (first, current, second);
} else if current > third {
return (first, second, current);
}
(first, second, third)
}
And you heard that it's better to not do return
, so you refactored it to:
fn get_top_three(current: i32, first: i32, second: i32, third: i32) -> (i32, i32, i32) {
let top_three: (i32, i32, i32);
if current > first {
top_three = (current, first, second);
} else if current > second {
top_three = (first, current, second);
} else if current > third {
top_three = (first, second, current);
} else {
top_three = (first, second, third)
}
top_three
}
And now your question is, is this better? If yes, why? What could you do differently?
The answer is: Yes and no. What you are missing is that everything can be used as a return expression. Including, and very important in this case, if
statements.
So you can rewrite the entire function like this:
fn get_top_three(current: i32, first: i32, second: i32, third: i32) -> (i32, i32, i32) {
if current > first {
(current, first, second)
} else if current > second {
(first, current, second)
} else if current > third {
(first, second, current)
} else {
(first, second, third)
}
}
And maybe with this in mind, you now realize how powerful those expressions really are :)
So why does this work?
Everything in Rust has a value. For example:
{}
has a value:
fn main() {
let x = {
let y = 10;
y
};
println!("{}", x);
}
10
if
-else
has a value:
fn main() {
let condition = false;
let y = if condition { 5 } else { 15 };
println!("{}", y);
}
15
And so on. Basically every block of code that is surrounded by {}
has a value, and you can set its value by a value expression in its last line. Just like at the end of a function. And you can assign its value directly to a variable, or forward it to its surrounding code block (like the way I refactored your code, where the value of the if
block gets directly forwarded to the function).
That is what makes this language feature so powerful.