The goal of this post is to learn how to define a function and control instructions flow.
A good example of a simple function is in the hello_world
project described in the previous post:
fn main() {
println!("Hello, world!");
}
Notice the fn
keyword to start the definition of the function. But in this example, main()
does not accept arguments nor return anything.
Maybe a more interesting example would be a function that generates a random integer in a range:
fn get_secret_number(nmin: i32, nmax: i32) -> i32 {
use rand::Rng;
// gen_range(low, high) is inclusive of low but exclusive of high
return rand::thread_rng().gen_range(nmin, nmax + 1);
}
rand
is an external package so the[dependencies]
section of theCargo.toml
configuration file needs to be updated like so:... [dependencies] rand = "0.6.0"
This function accepts two arguments called nmin
and nmax
of type i32
(signed integer that takes 32 bit of space) and its return type is specified after the arrow -> i32
.
In the above example, the result is returned explicitly with return
but in Rust, the return value of the function is synonymous with the value of the final expression. Meaning that get_secret_number()
can be modified as follows:
fn get_secret_number(nmin: i32, nmax: i32) -> i32 {
use rand::Rng;
rand::thread_rng().gen_range(nmin, nmax + 1)
}
Please note that with this syntax, the semicolon ;
is removed to prevent the expression to become a statement (which does not return a value).
Here is a complete example:
fn get_secret_number(nmin: i32, nmax: i32) -> i32 {
use rand::Rng;
rand::thread_rng().gen_range(nmin, nmax + 1)
}
fn main() {
let secret_number = get_secret_number(1, 100); // function call here!
println!("The secret number is {}.", secret_number);
}
In Rust, branching is mainly done with the keyword if
, the block of code that follows is executed only if the described condition is evaluated to true
:
fn strip_line(line: &mut String) {
// enter the code block only if the last character of the String is '\n'
if line.ends_with("\n") {
line.pop(); // remove this last character '\n'
// enter the code block only if the last character of the String is '\r'
if line.ends_with("\r") {
line.pop(); // and remove this last character '\r'
}
}
}
Multiple instruction branches can be chained together with the keywords else if
and else
:
fn main() {
let secret_number = get_secret_number(1, 100);
let guess = 50;
if guess < secret_number {
println!("The secret number is greater.");
}
else if guess > secret_number {
println!("The secret number is lower.");
}
else {
println!("Congratulations! The secret number {} is found.", guess);
}
}
A particular Rust mechanic can simplify the code above. It’s called pattern matching and it is based on the match keyword. Please note that each matching arm is evaluated and all possible values must be covered:
fn main() {
let secret_number = get_secret_number(1, 100);
let guess = 50;
// the cmp() function returns an Ordering enumeration
match guess.cmp(&secret_number) {
std::cmp::Ordering::Less => println!("The secret number is greater."),
std::cmp::Ordering::Greater => println!("The secret number is lower."),
std::cmp::Ordering::Equal => println!("Congratulations! The secret number {} is found.", guess),
};
}
An
enum
, in its simplest form, holds a set of mutually exclusive values:enum Ordering { Less, Greater, Equal, }
In Rust, it’s possible to repeat instructions by using the keywords for
, while
or loop
.
The for
-loop is very useful to iterate through a Range
of values:
fn main() {
println!("Start countdown...");
// rev() reverse the given range
for number in (1..10).rev() {
println!("{}...", number);
}
println("Let's go!");
}
Or through the items of a Collection
:
fn main() {
let a = [10, 20, 30, 40, 50];
for (index, element) in a.iter().enumerate() {
println!("The item at {} is {}.", index, element);
}
}
The while
-loop repeats the block of code as long as the specified condition holds true
:
fn main() {
let mut n: u32 = 10;
while n > 0 {
n -= 1;
}
println!("Done!");
}
And finally, there is the loop
keyword to create an infinite loop:
fn is_ready() -> bool {
use rand::Rng;
return rand::thread_rng().gen::<bool>()
}
fn main() {
loop {
println!("Work in progress...");
if is_ready() {
break; // leave the loop when the condition is met
}
}
}
Material for this post is available on GitHub.
More infos on the Rust documentation.
Next post available soon.