Book Image

Rust Programming Cookbook

By : Claus Matzinger
Book Image

Rust Programming Cookbook

By: Claus Matzinger

Overview of this book

Rust 2018, Rust's first major milestone since version 1.0, brings more advancement in the Rust language. The Rust Programming Cookbook is a practical guide to help you overcome challenges when writing Rust code. This Rust book covers recipes for configuring Rust for different environments and architectural designs, and provides solutions to practical problems. It will also take you through Rust's core concepts, enabling you to create efficient, high-performance applications that use features such as zero-cost abstractions and improved memory management. As you progress, you'll delve into more advanced topics, including channels and actors, for building scalable, production-grade applications, and even get to grips with error handling, macros, and modularization to write maintainable code. You will then learn how to overcome common roadblocks when using Rust for systems programming, IoT, web development, and network programming. Finally, you'll discover what Rust 2018 has to offer for embedded programmers. By the end of the book, you'll have learned how to build fast and safe applications and services using Rust.
Table of Contents (12 chapters)

Controlling execution flow

In Rust, controlling the execution flow of a program goes beyond simple if and while statements. We will see how to do that in this recipe.

How to do it...

For this recipe, the steps are as follows:

  1. Create a new project using cargo new execution-flow -- lib and open the project in an editor.
  2. Basic conditionals such as if statements work just like in any other language, so let's start with those and replace the default mod tests { ... } statement in the file:
#[cfg(test)]
mod tests {
#[test]
fn conditionals() {
let i = 20;
// Rust's if statement does not require parenthesis
if i < 2 {
assert!(i < 2);
} else if i > 2 {
assert!(i > 2);
} else {
assert_eq!(i, 2);
}
}
}
  1. Conditionals in Rust can do much more! Here is an additional test to show what they can do—add it before the last closing parenthesis:
    #[test]
fn more_conditionals() {
let my_option = Some(10);

// If let statements can do simple pattern matching
if let Some(unpacked) = my_option {
assert_eq!(unpacked, 10);
}

let mut other_option = Some(2);
// there is also while let, which does the same thing
while let Some(unpacked) = other_option {

// if can also return values in assignments
other_option = if unpacked > 0 {
Some(unpacked - 1)
} else {
None
}
}
assert_eq!(other_option, None)
}
  1. A conditional isn't the only statement that can be used to change the flow of execution. There is, of course, also the loop and its variations. Let's add another test for those as well, starting with a few basics:
    #[test]
fn loops() {

let mut i = 42;
let mut broke = false;

// a basic loop with control statements
loop {
i -= 1;
if i < 2 {
broke = true;
break;
} else if i > 2 {
continue;
}
}
assert!(broke);

// loops and other constructs can be named for better
readability ...
'outer: loop {
'inner: loop {
break 'inner; // ... and specifically jumped out of
}
break 'outer;
}
  1. Next, we will add more code to the test to see that loops are regular statements that can return values and that ranges can be used in for loops as well:
        let mut iterations: u32 = 0;

let total_squared = loop {
iterations += 1;

if iterations >= 10 {
break iterations.pow(2);
}
};
assert_eq!(total_squared, 100);

for i in 0..10 {
assert!(i >= 0 && i < 10)
}

for v in vec![1, 1, 1, 1].iter() {
assert_eq!(v, &1);
}
}

  1. With these three tests prepared, let's run cargo test to see them working:
$ cargo test
Compiling execution-flow v0.1.0 (Rust-Cookbook/Chapter01/execution-flow)
warning: value assigned to `broke` is never read
--> src/lib.rs:20:17
|
20 | let mut broke = false;
| ^^^^^
|
= note: #[warn(unused_assignments)] on by default
= help: maybe it is overwritten before being read?

Finished dev [unoptimized + debuginfo] target(s) in 0.89s
Running target/debug/deps/execution_flow-5a5ee2c7dd27585c

running 3 tests
test tests::conditionals ... ok
test tests::loops ... ok
test tests::more_conditionals ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Now, let's go behind the scenes to understand the code better.

How it works...

Although not vastly different from many languages' control statements, the basic constructs in Rust can change the way you think about variable assignments. It certainly transformed our mental models to be more data-focused. This means that instead of thinking if this condition is reached, assign this other value to a variable, a reversed assign this other value to a variable if this condition is reached—or shorter transform this variable if this condition applies—may take over.

This is the functional stream in the Rust programming language and it lends itself well to shortening and focusing the important parts of a piece of code. Similar implications can be made from the loop constructs since everything is a scope and has a return value. Using these capabilities will make every program a lot more readable and shorter, especially if it's just simple operations.

We've successfully learned how to control execution flow. Now, let's move on to the next recipe.