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)

Sequence types in Rust

Sequences are supported in many forms in Rust. The regular array is strictly implemented: it has to be defined at compile time (using literals) and be of a single data type, and cannot change in size. Tuples can have members of different types, but cannot change in size either. Vec<T> is a generic sequence type (of whatever you define as type T) that provides dynamic resizing—but T can only be of a single type. All in all, each of them has its purpose and, in this recipe, we will explore each.

How to do it...

The steps for this recipe are as follows:

  1. Use cargo to create a new project, cargo new sequences --lib, or clone it from this book's GitHub repository (https://github.com/PacktPublishing/Rust-Programming-Cookbook). Use Visual Studio Code and Terminal to open the project's directory.
  2. With the test module ready, let's start with arrays. Arrays in Rust have a familiar syntax but they follow a stricter definition. We can try out various abilities of the Rust array in a test:
    #[test]
fn exploring_arrays() {
let mut arr: [usize; 3] = [0; 3];
assert_eq!(arr, [0, 0, 0]);

let arr2: [usize; 5] = [1,2,3,4,5];
assert_eq!(arr2, [1,2,3,4,5]);

arr[0] = 1;
assert_eq!(arr, [1, 0, 0]);
assert_eq!(arr[0], 1);
assert_eq!(mem::size_of_val(&arr), mem::size_of::<usize>()
* 3);
}
  1. Users of more recent programming languages and data science/math environments will also be familiar with the tuple, a fixed-size variable type collection. Add a test for working with tuples:
    struct Point(f32, f32);

#[test]
fn exploring_tuples() {
let mut my_tuple: (i32, usize, f32) = (10, 0, -3.42);

assert_eq!(my_tuple.0, 10);
assert_eq!(my_tuple.1, 0);
assert_eq!(my_tuple.2, -3.42);

my_tuple.0 = 100;
assert_eq!(my_tuple.0, 100);

let (_val1, _val2, _val3) = my_tuple;

let point = Point(1.2, 2.1);
assert_eq!(point.0, 1.2);
assert_eq!(point.1, 2.1);
}
  1. As the last collection, the vector is the basis for all of the other quick and expandable data types. Create the following test with several assertions that show how to use the vec! macro and the vector's memory usage:
    use std::mem;

#[test]
fn exploring_vec() {
assert_eq!(vec![0; 3], [0, 0, 0]);
let mut v: Vec<i32> = vec![];

assert_eq!(mem::size_of::<Vec<i32>>(),
mem::size_of::<usize>
() * 3);

assert_eq!(mem::size_of_val(&*v), 0);

v.push(10);

assert_eq!(mem::size_of::<Vec<i32>>(),
mem::size_of::<i32>() * 6);

The remainder of the test shows how to modify and read the vector:

        assert_eq!(v[0], 10);

v.insert(0, 11);
v.push(12);
assert_eq!(v, [11, 10, 12]);
assert!(!v.is_empty());

assert_eq!(v.swap_remove(0), 11);
assert_eq!(v, [12, 10]);

assert_eq!(v.pop(), Some(10));
assert_eq!(v, [12]);

assert_eq!(v.remove(0), 12);

v.shrink_to_fit();
assert_eq!(mem::size_of_val(&*v), 0);
}

  1. Run cargo test to see the working tests run:
$ cargo test
Compiling sequences v0.1.0 (Rust-Cookbook/Chapter01/sequences)
Finished dev [unoptimized + debuginfo] target(s) in 1.28s
Running target/debug/deps/sequences-f931e7184f2b4f3d

running 3 tests
test tests::exploring_arrays ... ok
test tests::exploring_tuples ... ok
test tests::exploring_vec ... ok

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

Doc-tests sequences

running 0 tests

test result: ok. 0 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...

Sequence types are compound types that allocate a continuous part of the memory for faster and easier access. Vec<T> creates a simple, heap-allocated version of an array that grows (and shrinks) dynamically (step 4).

The original array (step 2) allocates memory on the stack and has to have a known size at compile time, which is a significant factor in using it. Both can be iterated and viewed using slices (https://doc.rust-lang.org/book/ch04-03-slices.html).

Tuples (step 3) are a different beast since they don't lend themselves to slices and are more a group of variables that have a semantic relationship—like a point in a two-dimensional space. Another use case is to return more than one variable to the caller of a function without the use of an additional struct or misusing a collection type.

Sequences in Rust are special because of the low overhead they produce. The size of Vec<T> is a pointer to an n * size of T memory on the heap, along with the size of the allocated memory, and how much of that is used. For arrays, the capacity is the current size (which the compiler can fill in during compilation), and tuples are more or less syntactic sugar on top of three distinct variables. Each of the three types provides convenience functions to change the contents—and, in the case of Vec<T>, the size of the collection. We recommend taking a close look at the tests and their comments to find out more about each type.

We have covered the basics of sequences in Rust, so let's move on to the next recipe.