0% found this document useful (0 votes)
57 views226 pages

Comprehensive Rust

Comprehensive Rust is a three-day course designed by the Android team to teach Rust programming, covering topics from basic syntax to advanced concepts like generics and error handling. The course includes specialized sessions on using Rust for Android development, bare-metal programming, and concurrency. Participants are expected to have prior programming knowledge, and the course emphasizes interactive learning and practical exercises.

Uploaded by

lucbrouillard381
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
57 views226 pages

Comprehensive Rust

Comprehensive Rust is a three-day course designed by the Android team to teach Rust programming, covering topics from basic syntax to advanced concepts like generics and error handling. The course includes specialized sessions on using Rust for Android development, bare-metal programming, and concurrency. Participants are expected to have prior programming knowledge, and the course emphasizes interactive learning and practical exercises.

Uploaded by

lucbrouillard381
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 226

Comprehensive R... https://google.github.io/comprehensive-rust/print.

html

Welcome to Comprehensive Rust �


build passing contributors 164 stars 19k

This is a three day Rust course developed by the Android team. The course covers the full
spectrum of Rust, from basic syntax to advanced topics like generics and error handling. It
also includes Android-specific content on the last day.

The goal of the course is to teach you Rust. We assume you don’t know anything about Rust
and hope to:

• Give you a comprehensive understanding of the Rust syntax and language.


• Enable you to modify existing programs and write new programs in Rust.
• Show you common Rust idioms.

The first three days show you the fundamentals of Rust. Following this, you’re invited to dive
into one or more specialized topics:

• Android: a half-day course on using Rust for Android platform development (AOSP).
This includes interoperability with C, C++, and Java.
• Bare-metal: a whole-day class on using Rust for bare-metal (embedded) development.
Both microcontrollers and application processors are covered.
• Concurrency: a whole-day class on concurrency in Rust. We cover both classical
concurrency (preemptively scheduling using threads and mutexes) and async/await
concurrency (cooperative multitasking using futures).

Non-Goals
Rust is a large language and we won’t be able to cover all of it in a few days. Some non-goals
of this course are:

• Learning how to develop macros: please see Chapter 19.5 in the Rust Book and Rust by
Example instead.

Assumptions
This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
The course
traffic. assumes
Learn more thatgot
OK, youit already know how to program. Rust is a statically- typed

1 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

language and we will sometimes make comparisons with C and C++ to better explain or
contrast the Rust approach.

If you know how to program in a dynamically-typed language such as Python or JavaScript,


then you will be able to follow along just fine too.

▸ Speaker Notes

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

2 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Running the Course

This page is for the course instructor.

Here is a bit of background information about how we’ve been running the course internally
at Google.

Before you run the course, you will want to:

1. Make yourself familiar with the course material. We’ve included speaker notes to help
highlight the key points (please help us by contributing more speaker notes!). When
presenting, you should make sure to open the speaker notes in a popup (click the link
with a little arrow next to “Speaker Notes”). This way you have a clean screen to
present to the class.

2. Decide on the dates. Since the course takes at least three full days, we recommend
that you schedule the days over two weeks. Course participants have said that they
find it helpful to have a gap in the course since it helps them process all the
information we give them.

3. Find a room large enough for your in-person participants. We recommend a class size
of 15-25 people. That’s small enough that people are comfortable asking questions —
it’s also small enough that one instructor will have time to answer the questions. Make
sure the room has desks for yourself and for the students: you will all need to be able
to sit and work with your laptops. In particular, you will be doing a lot of live-coding as
an instructor, so a lectern won’t be very helpful for you.

4. On the day of your course, show up to the room a little early to set things up. We
recommend presenting directly using mdbook serve running on your laptop (see the
installation instructions). This ensures optimal performance with no lag as you change
pages. Using your laptop will also allow you to fix typos as you or the course
participants spot them.

5. Let people solve the exercises by themselves or in small groups. We typically spend
30-45 minutes on exercises in the morning and in the afternoon (including time to
review the solutions). Make sure to ask people if they’re stuck or if there is anything
you can help with. When you see that several people have the same problem, call it out
to the class and offer a solution, e.g., by showing people where to find the relevant
information in the standard library.
This site uses cookies from Google to deliver and enhance the quality of its services and to analyze

That is all,
traffic. good
Learn luck OK,
more running
got it the course! We hope it will be as much fun for you as it has

3 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

been for us!

Please provide feedback afterwards so that we can keep improving the course. We would
love to hear what worked well for you and what can be made better. Your students are also
very welcome to send us feedback!

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

4 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Course Structure

This page is for the course instructor.

The course is fast paced and covers a lot of ground:

• Day 1: Basic Rust, ownership and the borrow checker.


• Day 2: Compound data types, pattern matching, the standard library.
• Day 3: Traits and generics, error handling, testing, unsafe Rust.

Deep Dives
In addition to the 3-day class on Rust Fundamentals, we cover some more specialized topics:

Android

The Android Deep Dive is a half-day course on using Rust for Android platform
development. This includes interoperability with C, C++, and Java.

You will need an AOSP checkout. Make a checkout of the course repository on the same
machine and move the src/android/ directory into the root of your AOSP checkout. This
will ensure that the Android build system sees the Android.bp files in src/android/ .

Ensure that adb sync works with your emulator or real device and pre-build all Android
examples using src/android/build_all.sh . Read the script to see the commands it runs
and make sure they work when you run them by hand.

Bare-Metal

The Bare-Metal Deep Dive: a full day class on using Rust for bare-metal (embedded)
development. Both microcontrollers and application processors are covered.

For the microcontroller part, you will need to buy the BBC micro:bit v2 development board
ahead of time. Everybody will need to install a number of packages as described on the
welcome
This page.
site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

5 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Concurrency

The Concurrency Deep Dive is a full day class on classical as well as async / await
concurrency.

You will need a fresh crate set up and the dependencies downloaded and ready to go. You
can then copy/paste the examples into src/main.rs to experiment with them:

cargo init concurrency


cd concurrency
cargo add tokio --features full
cargo run

Format
The course is meant to be very interactive and we recommend letting the questions drive
the exploration of Rust!

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

6 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Keyboard Shortcuts
There are several useful keyboard shortcuts in mdBook:

• Arrow-Left : Navigate to the previous page.


• Arrow-Right : Navigate to the next page.
• Ctrl + Enter : Execute the code sample that has focus.
• s : Activate the search bar.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

7 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Translations
The course has been translated into other languages by a set of wonderful volunteers:

• Brazilian Portuguese by @rastringer, @hugojacob, @joaovicmendes and @henrif75.


• Korean by @keispace, @jiyongp and @jooyunghan.

Use the language picker in the top-right corner to switch between languages.

Incomplete Translations
There is a large number of in-progress translations. We link to the most recently updated
translations:

• Bengali by @raselmandol.
• French by @KookaS and @vcaen.
• German by @Throvn and @ronaldfw.
• Japanese by @CoinEZ-JPN and @momotaro1105.

If you want to help with this effort, please see our instructions for how to get going.
Translations are coordinated on the issue tracker.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

8 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Using Cargo
When you start reading about Rust, you will soon meet Cargo, the standard tool used in the
Rust ecosystem to build and run Rust applications. Here we want to give a brief overview of
what Cargo is and how it fits into the wider ecosystem and how it fits into this training.

Installation

Please follow the instructions on https://rustup.rs/.

This will give you the Cargo build tool ( cargo ) and the Rust compiler ( rustc ). You will also
get rustup , a command line utility that you can use to install/switch toolchains, setup cross
compilation, etc.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

9 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

The Rust Ecosystem


The Rust ecosystem consists of a number of tools, of which the main ones are:

• rustc : the Rust compiler which turns .rs files into binaries and other intermediate
formats.

• cargo : the Rust dependency manager and build tool. Cargo knows how to download
dependencies, usually hosted on https://crates.io, and it will pass them to rustc when
building your project. Cargo also comes with a built-in test runner which is used to
execute unit tests.

• rustup : the Rust toolchain installer and updater. This tool is used to install and update
rustc and cargo when new versions of Rust is released. In addition, rustup can also
download documentation for the standard library. You can have multiple versions of
Rust installed at once and rustup will let you switch between them as needed.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

10 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Code Samples in This Training


For this training, we will mostly explore the Rust language through examples which can be
executed through your browser. This makes the setup much easier and ensures a consistent
experience for everyone.

Installing Cargo is still encouraged: it will make it easier for you to do the exercises. On the
last day, we will do a larger exercise which shows you how to work with dependencies and
for that you need Cargo.

The code blocks in this course are fully interactive:

1 fn main() {
2 println!("Edit me!");
3 }

You can use Ctrl + Enter to execute the code when focus is in the text box.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

11 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Running Code Locally with Cargo


If you want to experiment with the code on your own system, then you will need to first
install Rust. Do this by following the instructions in the Rust Book. This should give you a
working rustc and cargo . At the time of writing, the latest stable Rust release has these
version numbers:

% rustc --version
rustc 1.69.0 (84c898d65 2023-04-16)
% cargo --version
cargo 1.69.0 (6e9a83356 2023-04-12)

With this in place, follow these steps to build a Rust binary from one of the examples in this
training:

1. Click the “Copy to clipboard” button on the example you want to copy.

2. Use cargo new exercise to create a new exercise/ directory for your code:

$ cargo new exercise


Created binary (application) `exercise` package

3. Navigate into exercise/ and use cargo run to build and run your binary:

$ cd exercise
$ cargo run
Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)
Finished dev [unoptimized + debuginfo] target(s) in 0.75s
Running `target/debug/exercise`
Hello, world!

4. Replace the boiler-plate code in src/main.rs with your own code. For example, using
the example on the previous page, make src/main.rs look like

fn main() {
println!("Edit me!");
}

5. Use cargo run to build and run your updated binary:


This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

12 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

$ cargo run
Compiling exercise v0.1.0 (/home/mgeisler/tmp/exercise)
Finished dev [unoptimized + debuginfo] target(s) in 0.24s
Running `target/debug/exercise`
Edit me!

6. Use cargo check to quickly check your project for errors, use cargo build to compile
it without running it. You will find the output in target/debug/ for a normal debug
build. Use cargo build --release to produce an optimized release build in
target/release/ .

7. You can add dependencies for your project by editing Cargo.toml . When you run
cargo commands, it will automatically download and compile missing dependencies
for you.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

13 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Welcome to Day 1
This is the first day of Comprehensive Rust. We will cover a lot of ground today:

• Basic Rust syntax: variables, scalar and compound types, enums, structs, references,
functions, and methods.

• Memory management: stack vs heap, manual memory management, scope-based


memory management, and garbage collection.

• Ownership: move semantics, copying and cloning, borrowing, and lifetimes.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

14 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

What is Rust?
Rust is a new programming language which had its 1.0 release in 2015:

• Rust is a statically compiled language in a similar role as C++


◦ rustc uses LLVM as its backend.
• Rust supports many platforms and architectures:
◦ x86, ARM, WebAssembly, …
◦ Linux, Mac, Windows, …
• Rust is used for a wide range of devices:
◦ firmware and boot loaders,
◦ smart displays,
◦ mobile phones,
◦ desktops,
◦ servers.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

15 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Hello World!
Let us jump into the simplest possible Rust program, a classic Hello World program:

1 fn main() {
2 println!("Hello �!");
3 }

What you see:

• Functions are introduced with fn .


• Blocks are delimited by curly braces like in C and C++.
• The main function is the entry point of the program.
• Rust has hygienic macros, println! is an example of this.
• Rust strings are UTF-8 encoded and can contain any Unicode character.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

16 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Small Example
Here is a small example program in Rust:

1 fn main() { // Program entry point


2 let mut x: i32 = 6; // Mutable variable binding
3 print!("{x}"); // Macro for printing, like printf
4 while x != 1 { // No parenthesis around expression
5 if x % 2 == 0 { // Math like in other languages
6 x = x / 2;
7 } else {
8 x = 3 * x + 1;
9 }
10 print!(" -> {x}");
11 }
12 println!();
13 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

17 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Why Rust?
Some unique selling points of Rust:

• Compile time memory safety.


• Lack of undefined runtime behavior.
• Modern language features.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

18 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Compile Time Guarantees


Static memory management at compile time:

• No uninitialized variables.
• No memory leaks (mostly, see notes).
• No double-frees.
• No use-after-free.
• No NULL pointers.
• No forgotten locked mutexes.
• No data races between threads.
• No iterator invalidation.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

19 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Runtime Guarantees
No undefined behavior at runtime:

• Array access is bounds checked.


• Integer overflow is defined (panic or wrap-around).

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

20 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Modern Features
Rust is built with all the experience gained in the last decades.

Language Features
• Enums and pattern matching.
• Generics.
• No overhead FFI.
• Zero-cost abstractions.

Tooling
• Great compiler errors.
• Built-in dependency manager.
• Built-in support for testing.
• Excellent Language Server Protocol support.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

21 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Basic Syntax
Much of the Rust syntax will be familiar to you from C, C++ or Java:

• Blocks and scopes are delimited by curly braces.


• Line comments are started with // , block comments are delimited by /* ... */ .
• Keywords like if and while work the same.
• Variable assignment is done with = , comparison is done with == .

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

22 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Scalar Types
Types Literals
i8 , i16 , i32 , i64 , i128 , -10 , 0 , 1_000 ,
Signed integers
isize 123_i64

u8 , u16 , u32 , u64 , u128 ,


Unsigned integers 0 , 123 , 10_u16
usize

Floating point 3.14 , -10.0e20 ,


f32 , f64
numbers 2_f32

Strings &str "foo" , "two\nlines"

Unicode scalar
char 'a' , 'α' , '∞'
values
Booleans bool true , false

The types have widths as follows:

• iN , uN , and fN are N bits wide,


• isize and usize are the width of a pointer,
• char is 32 bits wide,
• bool is 8 bits wide.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

23 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Compound Types
Types Literals
Arrays [T; N] [20, 30, 40] , [0; 3]

Tuples () , (T,) , (T1, T2) , … () , ('x',) , ('x', 1.2) , …

Array assignment and access:

1 fn main() {
2 let mut a: [i8; 10] = [42; 10];
3 a[5] = 0;
4 println!("a: {:?}", a);
5 }

Tuple assignment and access:

1 fn main() {
2 let t: (i8, bool) = (7, true);
3 println!("1st index: {}", t.0);
4 println!("2nd index: {}", t.1);
5 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

24 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

References
Like C++, Rust has references:

1 fn main() {
2 let mut x: i32 = 10;
3 let ref_x: &mut i32 = &mut x;
4 *ref_x = 20;
5 println!("x: {x}");
6 }

Some notes:

• We must dereference ref_x when assigning to it, similar to C and C++ pointers.
• Rust will auto-dereference in some cases, in particular when invoking methods (try
ref_x.count_ones() ).
• References that are declared as mut can be bound to different values over their
lifetime.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

25 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Dangling References
Rust will statically forbid dangling references:

1 fn main() {
2 let ref_x: &i32;
3 {
4 let x: i32 = 10;
5 ref_x = &x;
6 }
7 println!("ref_x: {ref_x}");
8 }

• A reference is said to “borrow” the value it refers to.


• Rust is tracking the lifetimes of all references to ensure they live long enough.
• We will talk more about borrowing when we get to ownership.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

26 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Slices
A slice gives you a view into a larger collection:

1 fn main() {
2 let mut a: [i32; 6] = [10, 20, 30, 40, 50, 60];
3 println!("a: {a:?}");
4
5 let s: &[i32] = &a[2..4];
6
7 println!("s: {s:?}");
8 }

• Slices borrow data from the sliced type.


• Question: What happens if you modify a[3] right before printing s ?

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

27 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

String vs str
We can now understand the two string types in Rust:

1 fn main() {
2 let s1: &str = "World";
3 println!("s1: {s1}");
4
5 let mut s2: String = String::from("Hello ");
6 println!("s2: {s2}");
7 s2.push_str(s1);
8 println!("s2: {s2}");
9
10 let s3: &str = &s2[6..];
11 println!("s3: {s3}");
12 }

Rust terminology:

• &str an immutable reference to a string slice.


• String a mutable string buffer.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

28 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Functions
A Rust version of the famous FizzBuzz interview question:

1 fn main() {
2 print_fizzbuzz_to(20);
3 }
4
5 fn is_divisible(n: u32, divisor: u32) -> bool {
6 if divisor == 0 {
7 return false;
8 }
9 n % divisor == 0
10 }
11
12 fn fizzbuzz(n: u32) -> String {
13 let fizz = if is_divisible(n, 3) { "fizz" } else { "" };
14 let buzz = if is_divisible(n, 5) { "buzz" } else { "" };
15 if fizz.is_empty() && buzz.is_empty() {
16 return format!("{n}");
17 }
18 format!("{fizz}{buzz}")
19 }
20
21 fn print_fizzbuzz_to(n: u32) {
22 for i in 1..=n {
23 println!("{}", fizzbuzz(i));
24 }
25 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

29 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Rustdoc
All language items in Rust can be documented using special /// syntax.

1 /// Determine whether the first argument is divisible by the second argument.
2 ///
3 /// If the second argument is zero, the result is false.
4 fn is_divisible_by(lhs: u32, rhs: u32) -> bool {
5 if rhs == 0 {
6 return false; // Corner case, early return
7 }
8 lhs % rhs == 0 // The last expression in a block is the return value
9 }

The contents are treated as Markdown. All published Rust library crates are automatically
documented at docs.rs using the rustdoc tool. It is idiomatic to document all public items
in an API using this pattern.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

30 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Methods
Methods are functions associated with a type. The self argument of a method is an
instance of the type it is associated with:

1 struct Rectangle {
2 width: u32,
3 height: u32,
4 }
5
6 impl Rectangle {
7 fn area(&self) -> u32 {
8 self.width * self.height
9 }
10
11 fn inc_width(&mut self, delta: u32) {
12 self.width += delta;
13 }
14 }
15
16 fn main() {
17 let mut rect = Rectangle { width: 10, height: 5 };
18 println!("old area: {}", rect.area());
19 rect.inc_width(5);
20 println!("new area: {}", rect.area());
21 }

• We will look much more at methods in today’s exercise and in tomorrow’s class.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

31 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Function Overloading
Overloading is not supported:

• Each function has a single implementation:


◦ Always takes a fixed number of parameters.
◦ Always takes a single set of parameter types.
• Default values are not supported:
◦ All call sites have the same number of arguments.
◦ Macros are sometimes used as an alternative.

However, function parameters can be generic:

1 fn pick_one<T>(a: T, b: T) -> T {
2 if std::process::id() % 2 == 0 { a } else { b }
3 }
4
5 fn main() {
6 println!("coin toss: {}", pick_one("heads", "tails"));
7 println!("cash prize: {}", pick_one(500, 1000));
8 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

32 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Day 1: Morning Exercises


In these exercises, we will explore two parts of Rust:

• Implicit conversions between types.

• Arrays and for loops.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

33 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Implicit Conversions
Rust will not automatically apply implicit conversions between types (unlike C++). You can see
this in a program like this:

1 fn multiply(x: i16, y: i16) -> i16 {


2 x * y
3 }
4
5 fn main() {
6 let x: i8 = 15;
7 let y: i16 = 1000;
8
9 println!("{x} * {y} = {}", multiply(x, y));
10 }

The Rust integer types all implement the From<T> and Into<T> traits to let us convert
between them. The From<T> trait has a single from() method and similarly, the Into<T>
trait has a single into() method. Implementing these traits is how a type expresses that it
can be converted into another type.

The standard library has an implementation of From<i8> for i16 , which means that we
can convert a variable x of type i8 to an i16 by calling i16::from(x) . Or, simpler, with
x.into() , because From<i8> for i16 implementation automatically create an
implementation of Into<i16> for i8 .

The same applies for your own From implementations for your own types, so it is sufficient
to only implement From to get a respective Into implementation automatically.

1. Execute the above program and look at the compiler error.

2. Update the code above to use into() to do the conversion.

3. Change the types of x and y to other things (such as f32 , bool , i128 ) to see which
types you can convert to which other types. Try converting small types to big types and
the other way around. Check the standard library documentation to see if From<T> is
implemented for the pairs you check.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

34 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Arrays and for Loops


We saw that an array can be declared like this:

let array = [10, 20, 30];

You can print such an array by asking for its debug representation with {:?} :

1 fn main() {
2 let array = [10, 20, 30];
3 println!("array: {array:?}");
4 }

Rust lets you iterate over things like arrays and ranges using the for keyword:

1 fn main() {
2 let array = [10, 20, 30];
3 print!("Iterating over array:");
4 for n in array {
5 print!(" {n}");
6 }
7 println!();
8
9 print!("Iterating over range:");
10 for i in 0..3 {
11 print!(" {}", array[i]);
12 }
13 println!();
14 }

Use the above to write a function pretty_print which pretty-print a matrix and a function
transpose which will transpose a matrix (turn rows into columns):

⎛ ⎡ 1 2 3⎤ ⎞ ⎡ 1 4 7⎤
transpose ⎜ ⎢ 4 5 6⎥ ⎟ == ⎢ 2 5 8⎥
⎝ ⎣ 7 8 9⎦ ⎠ ⎣ 3 6 9⎦

Hard-code both functions to operate on 3 × 3 matrices.

Copy the code below to https://play.rust-lang.org/ and implement the functions:

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

35 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

// TODO: remove this when you're done with your implementation.


#![allow(unused_variables, dead_code)]

fn transpose(matrix: [[i32; 3]; 3]) -> [[i32; 3]; 3] {


unimplemented!()
}

fn pretty_print(matrix: &[[i32; 3]; 3]) {


unimplemented!()
}

fn main() {
let matrix = [
[101, 102, 103], // <-- the comment makes rustfmt add a newline
[201, 202, 203],
[301, 302, 303],
];

println!("matrix:");
pretty_print(&matrix);

let transposed = transpose(matrix);


println!("transposed:");
pretty_print(&transposed);
}

Bonus Question
Could you use &[i32] slices instead of hard-coded 3 × 3 matrices for your argument and
return types? Something like &[&[i32]] for a two-dimensional slice-of-slices. Why or why
not?

See the ndarray crate for a production quality implementation.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

36 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Variables
Rust provides type safety via static typing. Variable bindings are immutable by default:

1 fn main() {
2 let x: i32 = 10;
3 println!("x: {x}");
4 // x = 20;
5 // println!("x: {x}");
6 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

37 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Type Inference
Rust will look at how the variable is used to determine the type:

1 fn takes_u32(x: u32) {
2 println!("u32: {x}");
3 }
4
5 fn takes_i8(y: i8) {
6 println!("i8: {y}");
7 }
8
9 fn main() {
10 let x = 10;
11 let y = 20;
12
13 takes_u32(x);
14 takes_i8(y);
15 // takes_u32(y);
16 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

38 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Static and Constant Variables


Globally-scoped names for values can be given with static variables and constant definitions.

const
You can declare compile-time constants:

1 const DIGEST_SIZE: usize = 3;


2 const ZERO: Option<u8> = Some(42);
3
4 fn compute_digest(text: &str) -> [u8; DIGEST_SIZE] {
5 let mut digest = [ZERO.unwrap_or(0); DIGEST_SIZE];
6 for (idx, &b) in text.as_bytes().iter().enumerate() {
7 digest[idx % DIGEST_SIZE] = digest[idx % DIGEST_SIZE].wrapping_add(b
8 }
9 digest
10 }
11
12 fn main() {
13 let digest = compute_digest("Hello");
14 println!("Digest: {digest:?}");
15 }

According to the Rust RFC Book these are inlined upon use.

static
You can also declare static variables:

1 static BANNER: &str = "Welcome to RustOS 3.14";


2
3 fn main() {
4 println!("{BANNER}");
5 }

As noted in the Rust RFC Book, these are not inlined upon use and have an actual associated
memory location. This is useful for unsafe and embedded code, and the variable lives
through the entirety of the program execution. When a globally-scoped value does not have
a reason to need object identity, const is generally preferred.
This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
Because static variables are accessible from any thread, they need to be guarded, for
traffic. Learn more OK, got it

39 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

example by a Mutex , or accessed using unsafe code. We will look at mutating static data in
the chapter on Unsafe Rust.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

40 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Scopes and Shadowing


You can shadow variables, both those from outer scopes and variables from the same
scope:

1 fn main() {
2 let a = 10;
3 println!("before: {a}");
4
5 {
6 let a = "hello";
7 println!("inner scope: {a}");
8
9 let a = true;
10 println!("shadowed in inner scope: {a}");
11 }
12
13 println!("after: {a}");
14 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

41 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Memory Management
Traditionally, languages have fallen into two broad categories:

• Full control via manual memory management: C, C++, Pascal, …


• Full safety via automatic memory management at runtime: Java, Python, Go, Haskell, …

Rust offers a new mix:

Full control and safety via compile time enforcement of correct memory management.

It does this with an explicit ownership concept.

First, let’s refresh how memory management works.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

42 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

The Stack vs The Heap


• Stack: Continuous area of memory for local variables.

◦ Values have fixed sizes known at compile time.


◦ Extremely fast: just move a stack pointer.
◦ Easy to manage: follows function calls.
◦ Great memory locality.

• Heap: Storage of values outside of function calls.

◦ Values have dynamic sizes determined at runtime.


◦ Slightly slower than the stack: some book-keeping needed.
◦ No guarantee of memory locality.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

43 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Stack and Heap Example


Creating a String puts fixed-sized metadata on the stack and dynamically sized data, the
actual string, on the heap:

1 fn main() {
2 let s1 = String::from("Hello");
3 }

Stack Heap

s1

ptr H e l l o
len 5
capacity 5

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

44 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Manual Memory Management


You allocate and deallocate heap memory yourself.

If not done with care, this can lead to crashes, bugs, security vulnerabilities, and memory
leaks.

C Example
You must call free on every pointer you allocate with malloc :

void foo(size_t n) {
int* int_array = malloc(n * sizeof(int));
//
// ... lots of code
//
free(int_array);
}

Memory is leaked if the function returns early between malloc and free : the pointer is lost
and we cannot deallocate the memory. Worse, freeing the pointer twice, or accessing a freed
pointer can lead to exploitable security vulnerabilities.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

45 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Scope-Based Memory Management


Constructors and destructors let you hook into the lifetime of an object.

By wrapping a pointer in an object, you can free memory when the object is destroyed. The
compiler guarantees that this happens, even if an exception is raised.

This is often called resource acquisition is initialization (RAII) and gives you smart pointers.

C++ Example

void say_hello(std::unique_ptr<Person> person) {


std::cout << "Hello " << person->name << std::endl;
}

• The std::unique_ptr object is allocated on the stack, and points to memory allocated
on the heap.
• At the end of say_hello , the std::unique_ptr destructor will run.
• The destructor frees the Person object it points to.

Special move constructors are used when passing ownership to a function:

std::unique_ptr<Person> person = find_person("Carla");


say_hello(std::move(person));

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

46 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Automatic Memory Management


An alternative to manual and scope-based memory management is automatic memory
management:

• The programmer never allocates or deallocates memory explicitly.


• A garbage collector finds unused memory and deallocates it for the programmer.

Java Example
The person object is not deallocated after sayHello returns:

void sayHello(Person person) {


System.out.println("Hello " + person.getName());
}

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

47 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Memory Management in Rust


Memory management in Rust is a mix:

• Safe and correct like Java, but without a garbage collector.


• Depending on which abstraction (or combination of abstractions) you choose, can be a
single unique pointer, reference counted, or atomically reference counted.
• Scope-based like C++, but the compiler enforces full adherence.
• A Rust user can choose the right abstraction for the situation, some even have no cost
at runtime like C.

Rust achieves this by modeling ownership explicitly.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

48 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Comparison
Here is a rough comparison of the memory management techniques.

Pros of Different Memory Management Techniques


• Manual like C:
◦ No runtime overhead.
• Automatic like Java:
◦ Fully automatic.
◦ Safe and correct.
• Scope-based like C++:
◦ Partially automatic.
◦ No runtime overhead.
• Compiler-enforced scope-based like Rust:
◦ Enforced by compiler.
◦ No runtime overhead.
◦ Safe and correct.

Cons of Different Memory Management Techniques


• Manual like C:
◦ Use-after-free.
◦ Double-frees.
◦ Memory leaks.
• Automatic like Java:
◦ Garbage collection pauses.
◦ Destructor delays.
• Scope-based like C++:
◦ Complex, opt-in by programmer (on C++).
◦ Circular references can lead to memory leaks
◦ Potential runtime overhead
• Compiler-enforced and scope-based like Rust:
◦ Some upfront complexity.
◦ Can
This site uses reject
cookies valid
from programs.
Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

49 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Ownership
All variable bindings have a scope where they are valid and it is an error to use a variable
outside its scope:

1 struct Point(i32, i32);


2
3 fn main() {
4 {
5 let p = Point(3, 4);
6 println!("x: {}", p.0);
7 }
8 println!("y: {}", p.1);
9 }

• At the end of the scope, the variable is dropped and the data is freed.
• A destructor can run here to free up resources.
• We say that the variable owns the value.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

50 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Move Semantics
An assignment will transfer ownership between variables:

1 fn main() {
2 let s1: String = String::from("Hello!");
3 let s2: String = s1;
4 println!("s2: {s2}");
5 // println!("s1: {s1}");
6 }

• The assignment of s1 to s2 transfers ownership.


• When s1 goes out of scope, nothing happens: it does not own anything.
• When s2 goes out of scope, the string data is freed.
• There is always exactly one variable binding which owns a value.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

51 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Moved Strings in Rust


1 fn main() {
2 let s1: String = String::from("Rust");
3 let s2: String = s1;
4 }

• The heap data from s1 is reused for s2 .


• When s1 goes out of scope, nothing happens (it has been moved from).

Before move to s2 :

Stack Heap

s1

ptr R u s t
len 4
capacity 4

After move to s2 :

Stack Heap

s1 (inaccessible)

ptr R u s t
len 4
capacity 4

s2

ptr
len 4
capacity 4

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

52 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Extra Work in Modern C++


Modern C++ solves this differently:

std::string s1 = "Cpp";
std::string s2 = s1; // Duplicate the data in s1.

• The heap data from s1 is duplicated and s2 gets its own independent copy.
• When s1 and s2 go out of scope, they each free their own memory.

Before copy-assignment:

Stack Heap

s1

ptr C p p
len 3
capacity 3

After copy-assignment:

Stack Heap

s1

ptr C p p
len 3
capacity 3

s2

ptr C p p
len 3
capacity 3

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

53 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Moves in Function Calls


When you pass a value to a function, the value is assigned to the function parameter. This
transfers ownership:

1 fn say_hello(name: String) {
2 println!("Hello {name}")
3 }
4
5 fn main() {
6 let name = String::from("Alice");
7 say_hello(name);
8 // say_hello(name);
9 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

54 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Copying and Cloning


While move semantics are the default, certain types are copied by default:

1 fn main() {
2 let x = 42;
3 let y = x;
4 println!("x: {x}");
5 println!("y: {y}");
6 }

These types implement the Copy trait.

You can opt-in your own types to use copy semantics:

1 #[derive(Copy, Clone, Debug)]


2 struct Point(i32, i32);
3
4 fn main() {
5 let p1 = Point(3, 4);
6 let p2 = p1;
7 println!("p1: {p1:?}");
8 println!("p2: {p2:?}");
9 }

• After the assignment, both p1 and p2 own their own data.


• We can also use p1.clone() to explicitly copy the data.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

55 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Borrowing
Instead of transferring ownership when calling a function, you can let a function borrow the
value:

1 #[derive(Debug)]
2 struct Point(i32, i32);
3
4 fn add(p1: &Point, p2: &Point) -> Point {
5 Point(p1.0 + p2.0, p1.1 + p2.1)
6 }
7
8 fn main() {
9 let p1 = Point(3, 4);
10 let p2 = Point(10, 20);
11 let p3 = add(&p1, &p2);
12 println!("{p1:?} + {p2:?} = {p3:?}");
13 }

• The add function borrows two points and returns a new point.
• The caller retains ownership of the inputs.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

56 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Shared and Unique Borrows


Rust puts constraints on the ways you can borrow values:

• You can have one or more &T values at any given time, or
• You can have exactly one &mut T value.

1 fn main() {
2 let mut a: i32 = 10;
3 let b: &i32 = &a;
4
5 {
6 let c: &mut i32 = &mut a;
7 *c = 20;
8 }
9
10 println!("a: {a}");
11 println!("b: {b}");
12 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

57 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Lifetimes
A borrowed value has a lifetime:

• The lifetime can be implicit: add(p1: &Point, p2: &Point) -> Point .
• Lifetimes can also be explicit: &'a Point , &'document str .
• Read &'a Point as “a borrowed Point which is valid for at least the lifetime a ”.
• Lifetimes are always inferred by the compiler: you cannot assign a lifetime yourself.
◦ Lifetime annotations create constraints; the compiler verifies that there is a valid
solution.
• Lifetimes for function arguments and return values must be fully specified, but Rust
allows lifetimes to be elided in most cases with a few simple rules.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

58 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Lifetimes in Function Calls


In addition to borrowing its arguments, a function can return a borrowed value:

1 #[derive(Debug)]
2 struct Point(i32, i32);
3
4 fn left_most<'a>(p1: &'a Point, p2: &'a Point) -> &'a Point {
5 if p1.0 < p2.0 { p1 } else { p2 }
6 }
7
8 fn main() {
9 let p1: Point = Point(10, 10);
10 let p2: Point = Point(20, 20);
11 let p3: &Point = left_most(&p1, &p2);
12 println!("left-most point: {:?}", p3);
13 }

• 'a is a generic parameter, it is inferred by the compiler.


• Lifetimes start with ' and 'a is a typical default name.
• Read &'a Point as “a borrowed Point which is valid for at least the lifetime a ”.
◦ The at least part is important when parameters are in different scopes.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

59 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Lifetimes in Data Structures


If a data type stores borrowed data, it must be annotated with a lifetime:

1 #[derive(Debug)]
2 struct Highlight<'doc>(&'doc str);
3
4 fn erase(text: String) {
5 println!("Bye {text}!");
6 }
7
8 fn main() {
9 let text = String::from("The quick brown fox jumps over the lazy dog.");
10 let fox = Highlight(&text[4..19]);
11 let dog = Highlight(&text[35..43]);
12 // erase(text);
13 println!("{fox:?}");
14 println!("{dog:?}");
15 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

60 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Day 1: Afternoon Exercises


We will look at two things:

• A small book library,

• Iterators and ownership (hard).

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

61 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Storing Books
We will learn much more about structs and the Vec<T> type tomorrow. For now, you just
need to know part of its API:

1 fn main() {
2 let mut vec = vec![10, 20];
3 vec.push(30);
4 let midpoint = vec.len() / 2;
5 println!("middle value: {}", vec[midpoint]);
6 for item in &vec {
7 println!("item: {item}");
8 }
9 }

Use this to model a library’s book collection. Copy the code below to https://play.rust-
lang.org/ and update the types to make it compile:

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

62 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

struct Library {
books: Vec<Book>,
}

struct Book {
title: String,
year: u16,
}

impl Book {
// This is a constructor, used below.
fn new(title: &str, year: u16) -> Book {
Book {
title: String::from(title),
year,
}
}
}

// Implement the methods below. Update the `self` parameter to


// indicate the method's required level of ownership over the object:
//
// - `&self` for shared read-only access,
// - `&mut self` for unique and mutable access,
// - `self` for unique access by value.
impl Library {
fn new() -> Library {
todo!("Initialize and return a `Library` value")
}

//fn len(self) -> usize {


// todo!("Return the length of `self.books`")
//}

//fn is_empty(self) -> bool {


// todo!("Return `true` if `self.books` is empty")
//}

//fn add_book(self, book: Book) {


// todo!("Add a new book to `self.books`")
//}

//fn print_books(self) {
// todo!("Iterate over `self.books` and each book's title and year")
//}

//fn oldest_book(self) -> Option<&Book> {


// todo!("Return a reference to the oldest book (if any)")
//}
}
This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
// This
traffic. shows
Learn moretheOK,
desired
got it behavior. Uncomment the code below and

63 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

// implement the missing methods. You will need to update the


// method signatures, including the "self" parameter! You may
// also need to update the variable bindings within main.
fn main() {
let library = Library::new();

//println!("The library is empty: library.is_empty() -> {}",


library.is_empty());
//
//library.add_book(Book::new("Lord of the Rings", 1954));
//library.add_book(Book::new("Alice's Adventures in Wonderland", 1865));
//
//println!("The library is no longer empty: library.is_empty() -> {}",
library.is_empty());
//
//
//library.print_books();
//
//match library.oldest_book() {
// Some(book) => println!("The oldest book is {}", book.title),
// None => println!("The library is empty!"),
//}
//
//println!("The library has {} books", library.len());
//library.print_books();
}

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

64 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Iterators and Ownership


The ownership model of Rust affects many APIs. An example of this is the Iterator and
IntoIterator traits.

Iterator
Traits are like interfaces: they describe behavior (methods) for a type. The Iterator trait
simply says that you can call next until you get None back:

pub trait Iterator {


type Item;
fn next(&mut self) -> Option<Self::Item>;
}

You use this trait like this:

1 fn main() {
2 let v: Vec<i8> = vec![10, 20, 30];
3 let mut iter = v.iter();
4
5 println!("v[0]: {:?}", iter.next());
6 println!("v[1]: {:?}", iter.next());
7 println!("v[2]: {:?}", iter.next());
8 println!("No more items: {:?}", iter.next());
9 }

What is the type returned by the iterator? Test your answer here:

1 fn main() {
2 let v: Vec<i8> = vec![10, 20, 30];
3 let mut iter = v.iter();
4
5 let v0: Option<..> = iter.next();
6 println!("v0: {v0:?}");
7 }

Why is this type used?

IntoIterator
This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it
The Iterator trait tells you how to iterate once you have created an iterator. The related

65 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

trait IntoIterator tells you how to create the iterator:

pub trait IntoIterator {


type Item;
type IntoIter: Iterator<Item = Self::Item>;

fn into_iter(self) -> Self::IntoIter;


}

The syntax here means that every implementation of IntoIterator must declare two types:

• Item : the type we iterate over, such as i8 ,


• IntoIter : the Iterator type returned by the into_iter method.

Note that IntoIter and Item are linked: the iterator must have the same Item type, which
means that it returns Option<Item>

Like before, what is the type returned by the iterator?

1 fn main() {
2 let v: Vec<String> = vec![String::from("foo"), String::from("bar")];
3 let mut iter = v.into_iter();
4
5 let v0: Option<..> = iter.next();
6 println!("v0: {v0:?}");
7 }

for Loops
Now that we know both Iterator and IntoIterator , we can build for loops. They call
into_iter() on an expression and iterates over the resulting iterator:

1 fn main() {
2 let v: Vec<String> = vec![String::from("foo"), String::from("bar")];
3
4 for word in &v {
5 println!("word: {word}");
6 }
7
8 for word in v {
9 println!("word: {word}");
10 }
11 }
This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
What is the type of word in each loop?
traffic. Learn more OK, got it

66 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Experiment with the code above and then consult the documentation for impl
IntoIterator for &Vec<T> and impl IntoIterator for Vec<T> to check your answers.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

67 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Welcome to Day 2
Now that we have seen a fair amount of Rust, we will continue with:

• Structs, enums, methods.

• Pattern matching: destructuring enums, structs, and arrays.

• Control flow constructs: if , if let , while , while let , break , and continue .

• The Standard Library: String , Option and Result , Vec , HashMap , Rc and Arc .

• Modules: visibility, paths, and filesystem hierarchy.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

68 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Structs
Like C and C++, Rust has support for custom structs:

1 struct Person {
2 name: String,
3 age: u8,
4 }
5
6 fn main() {
7 let mut peter = Person {
8 name: String::from("Peter"),
9 age: 27,
10 };
11 println!("{} is {} years old", peter.name, peter.age);
12
13 peter.age = 28;
14 println!("{} is {} years old", peter.name, peter.age);
15
16 let jackie = Person {
17 name: String::from("Jackie"),
18 ..peter
19 };
20 println!("{} is {} years old", jackie.name, jackie.age);
21 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

69 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Tuple Structs
If the field names are unimportant, you can use a tuple struct:

1 struct Point(i32, i32);


2
3 fn main() {
4 let p = Point(17, 23);
5 println!("({}, {})", p.0, p.1);
6 }

This is often used for single-field wrappers (called newtypes):

1 struct PoundsOfForce(f64);
2 struct Newtons(f64);
3
4 fn compute_thruster_force() -> PoundsOfForce {
5 todo!("Ask a rocket scientist at NASA")
6 }
7
8 fn set_thruster_force(force: Newtons) {
9 // ...
10 }
11
12 fn main() {
13 let force = compute_thruster_force();
14 set_thruster_force(force);
15 }
16

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

70 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Field Shorthand Syntax


If you already have variables with the right names, then you can create the struct using a
shorthand:

1 #[derive(Debug)]
2 struct Person {
3 name: String,
4 age: u8,
5 }
6
7 impl Person {
8 fn new(name: String, age: u8) -> Person {
9 Person { name, age }
10 }
11 }
12
13 fn main() {
14 let peter = Person::new(String::from("Peter"), 27);
15 println!("{peter:?}");
16 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

71 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Enums
The enum keyword allows the creation of a type which has a few different variants:

1 fn generate_random_number() -> i32 {


2 // Implementation based on https://xkcd.com/221/
3 4 // Chosen by fair dice roll. Guaranteed to be random.
4 }
5
6 #[derive(Debug)]
7 enum CoinFlip {
8 Heads,
9 Tails,
10 }
11
12 fn flip_coin() -> CoinFlip {
13 let random_number = generate_random_number();
14 if random_number % 2 == 0 {
15 return CoinFlip::Heads;
16 } else {
17 return CoinFlip::Tails;
18 }
19 }
20
21 fn main() {
22 println!("You got: {:?}", flip_coin());
23 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

72 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Variant Payloads
You can define richer enums where the variants carry data. You can then use the match
statement to extract the data from each variant:

1 enum WebEvent {
2 PageLoad, // Variant without payload
3 KeyPress(char), // Tuple struct variant
4 Click { x: i64, y: i64 }, // Full struct variant
5 }
6
7 #[rustfmt::skip]
8 fn inspect(event: WebEvent) {
9 match event {
10 WebEvent::PageLoad => println!("page loaded"),
11 WebEvent::KeyPress(c) => println!("pressed '{c}'"),
12 WebEvent::Click { x, y } => println!("clicked at x={x}, y={y}"),
13 }
14 }
15
16 fn main() {
17 let load = WebEvent::PageLoad;
18 let press = WebEvent::KeyPress('x');
19 let click = WebEvent::Click { x: 20, y: 80 };
20
21 inspect(load);
22 inspect(press);
23 inspect(click);
24 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

73 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Enum Sizes
Rust enums are packed tightly, taking constraints due to alignment into account:

1 use std::any::type_name;
2 use std::mem::{align_of, size_of};
3
4 fn dbg_size<T>() {
5 println!("{}: size {} bytes, align: {} bytes",
6 type_name::<T>(), size_of::<T>(), align_of::<T>());
7 }
8
9 enum Foo {
10 A,
11 B,
12 }
13
14 fn main() {
15 dbg_size::<Foo>();
16 }

• See the Rust Reference.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

74 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Methods
Rust allows you to associate functions with your new types. You do this with an impl block:

1 #[derive(Debug)]
2 struct Person {
3 name: String,
4 age: u8,
5 }
6
7 impl Person {
8 fn say_hello(&self) {
9 println!("Hello, my name is {}", self.name);
10 }
11 }
12
13 fn main() {
14 let peter = Person {
15 name: String::from("Peter"),
16 age: 27,
17 };
18 peter.say_hello();
19 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

75 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Method Receiver
The &self above indicates that the method borrows the object immutably. There are other
possible receivers for a method:

• &self : borrows the object from the caller using a shared and immutable reference.
The object can be used again afterwards.
• &mut self : borrows the object from the caller using a unique and mutable reference.
The object can be used again afterwards.
• self : takes ownership of the object and moves it away from the caller. The method
becomes the owner of the object. The object will be dropped (deallocated) when the
method returns, unless its ownership is explicitly transmitted. Complete ownership
does not automatically mean mutability.
• mut self : same as above, but the method can mutate the object.
• No receiver: this becomes a static method on the struct. Typically used to create
constructors which are called new by convention.

Beyond variants on self , there are also special wrapper types allowed to be receiver types,
such as Box<Self> .

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

76 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Example
1 #[derive(Debug)]
2 struct Race {
3 name: String,
4 laps: Vec<i32>,
5 }
6
7 impl Race {
8 fn new(name: &str) -> Race { // No receiver, a static method
9 Race { name: String::from(name), laps: Vec::new() }
10 }
11
12 fn add_lap(&mut self, lap: i32) { // Exclusive borrowed read-write access to se
13 self.laps.push(lap);
14 }
15
16 fn print_laps(&self) { // Shared and read-only borrowed access to self
17 println!("Recorded {} laps for {}:", self.laps.len(), self.name);
18 for (idx, lap) in self.laps.iter().enumerate() {
19 println!("Lap {idx}: {lap} sec");
20 }
21 }
22
23 fn finish(self) { // Exclusive ownership of self
24 let total = self.laps.iter().sum::<i32>();
25 println!("Race {} is finished, total lap time: {}", self.name, total
26 }
27 }
28
29 fn main() {
30 let mut race = Race::new("Monaco Grand Prix");
31 race.add_lap(70);
32 race.add_lap(68);
33 race.print_laps();
34 race.add_lap(71);
35 race.print_laps();
36 race.finish();
37 // race.add_lap(42);
38 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

77 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Pattern Matching
The match keyword let you match a value against one or more patterns. The comparisons
are done from top to bottom and the first match wins.

The patterns can be simple values, similarly to switch in C and C++:

1 fn main() {
2 let input = 'x';
3
4 match input {
5 'q' => println!("Quitting"),
6 'a' | 's' | 'w' | 'd' => println!("Moving around"),
7 '0'..='9' => println!("Number input"),
8 _ => println!("Something else"),
9 }
10 }

The _ pattern is a wildcard pattern which matches any value.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

78 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Destructuring Enums
Patterns can also be used to bind variables to parts of your values. This is how you inspect
the structure of your types. Let us start with a simple enum type:

1 enum Result {
2 Ok(i32),
3 Err(String),
4 }
5
6 fn divide_in_two(n: i32) -> Result {
7 if n % 2 == 0 {
8 Result::Ok(n / 2)
9 } else {
10 Result::Err(format!("cannot divide {n} into two equal parts"))
11 }
12 }
13
14 fn main() {
15 let n = 100;
16 match divide_in_two(n) {
17 Result::Ok(half) => println!("{n} divided in two is {half}"),
18 Result::Err(msg) => println!("sorry, an error happened: {msg}"),
19 }
20 }

Here we have used the arms to destructure the Result value. In the first arm, half is
bound to the value inside the Ok variant. In the second arm, msg is bound to the error
message.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

79 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Destructuring Structs
You can also destructure structs :

1 struct Foo {
2 x: (u32, u32),
3 y: u32,
4 }
5
6 #[rustfmt::skip]
7 fn main() {
8 let foo = Foo { x: (1, 2), y: 3 };
9 match foo {
10 Foo { x: (1, b), y } => println!("x.0 = 1, b = {b}, y = {y}"),
11 Foo { y: 2, x: i } => println!("y = 2, x = {i:?}"),
12 Foo { y, .. } => println!("y = {y}, other fields were ignored"
13 }
14 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

80 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Destructuring Arrays
You can destructure arrays, tuples, and slices by matching on their elements:

1 #[rustfmt::skip]
2 fn main() {
3 let triple = [0, -2, 3];
4 println!("Tell me about {triple:?}");
5 match triple {
6 [0, y, z] => println!("First is 0, y = {y}, and z = {z}"),
7 [1, ..] => println!("First is 1 and the rest were ignored"),
8 _ => println!("All elements were ignored"),
9 }
10 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

81 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Match Guards
When matching, you can add a guard to a pattern. This is an arbitrary Boolean expression
which will be executed if the pattern matches:

1 #[rustfmt::skip]
2 fn main() {
3 let pair = (2, -2);
4 println!("Tell me about {pair:?}");
5 match pair {
6 (x, y) if x == y => println!("These are twins"),
7 (x, y) if x + y == 0 => println!("Antimatter, kaboom!"),
8 (x, _) if x % 2 == 1 => println!("The first one is odd"),
9 _ => println!("No correlation..."),
10 }
11 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

82 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Day 2: Morning Exercises


We will look at implementing methods in two contexts:

• Simple struct which tracks health statistics.

• Multiple structs and enums for a drawing library.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

83 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Health Statistics
You’re working on implementing a health-monitoring system. As part of that, you need to
keep track of users’ health statistics.

You’ll start with some stubbed functions in an impl block as well as a User struct
definition. Your goal is to implement the stubbed out methods on the User struct defined
in the impl block.

Copy the code below to https://play.rust-lang.org/ and fill in the missing methods:

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

84 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

// TODO: remove this when you're done with your implementation.


#![allow(unused_variables, dead_code)]

pub struct User {


name: String,
age: u32,
height: f32,
visit_count: usize,
last_blood_pressure: Option<(u32, u32)>,
}

pub struct Measurements {


height: f32,
blood_pressure: (u32, u32),
}

pub struct HealthReport<'a> {


patient_name: &'a str,
visit_count: u32,
height_change: f32,
blood_pressure_change: Option<(i32, i32)>,
}

impl User {
pub fn new(name: String, age: u32, height: f32) -> Self {
unimplemented!()
}

pub fn name(&self) -> &str {


unimplemented!()
}

pub fn age(&self) -> u32 {


unimplemented!()
}

pub fn height(&self) -> f32 {


unimplemented!()
}

pub fn doctor_visits(&self) -> u32 {


unimplemented!()
}

pub fn set_age(&mut self, new_age: u32) {


unimplemented!()
}

pub fn set_height(&mut self, new_height: f32) {


unimplemented!()
This site
} uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

85 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

pub fn visit_doctor(&mut self, measurements: Measurements) -> HealthReport


{
unimplemented!()
}
}

fn main() {
let bob = User::new(String::from("Bob"), 32, 155.2);
println!("I'm {} and my age is {}", bob.name(), bob.age());
}

#[test]
fn test_height() {
let bob = User::new(String::from("Bob"), 32, 155.2);
assert_eq!(bob.height(), 155.2);
}

#[test]
fn test_set_age() {
let mut bob = User::new(String::from("Bob"), 32, 155.2);
assert_eq!(bob.age(), 32);
bob.set_age(33);
assert_eq!(bob.age(), 33);
}

#[test]
fn test_visit() {
let mut bob = User::new(String::from("Bob"), 32, 155.2);
assert_eq!(bob.doctor_visits(), 0);
let report = bob.visit_doctor(Measurements {
height: 156.1,
blood_pressure: (120, 80),
});
assert_eq!(report.patient_name, "Bob");
assert_eq!(report.visit_count, 1);
assert_eq!(report.blood_pressure_change, None);

let report = bob.visit_doctor(Measurements {


height: 156.1,
blood_pressure: (115, 76),
});

assert_eq!(report.visit_count, 2);
assert_eq!(report.blood_pressure_change, Some((-5, -4)));
}

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

86 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Polygon Struct
We will create a Polygon struct which contain some points. Copy the code below to
https://play.rust-lang.org/ and fill in the missing methods to make the tests pass:

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

87 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

// TODO: remove this when you're done with your implementation.


#![allow(unused_variables, dead_code)]

pub struct Point {


// add fields
}

impl Point {
// add methods
}

pub struct Polygon {


// add fields
}

impl Polygon {
// add methods
}

pub struct Circle {


// add fields
}

impl Circle {
// add methods
}

pub enum Shape {


Polygon(Polygon),
Circle(Circle),
}

#[cfg(test)]
mod tests {
use super::*;

fn round_two_digits(x: f64) -> f64 {


(x * 100.0).round() / 100.0
}

#[test]
fn test_point_magnitude() {
let p1 = Point::new(12, 13);
assert_eq!(round_two_digits(p1.magnitude()), 17.69);
}

#[test]
fn test_point_dist() {
let p1 = Point::new(10, 10);
let p2 = Point::new(14, 13);
This site uses cookies from Google to deliver and enhance the quality5.00);
assert_eq!(round_two_digits(p1.dist(p2)), of its services and to analyze
traffic. } Learn more OK, got it

88 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

#[test]
fn test_point_add() {
let p1 = Point::new(16, 16);
let p2 = p1 + Point::new(-4, 3);
assert_eq!(p2, Point::new(12, 19));
}

#[test]
fn test_polygon_left_most_point() {
let p1 = Point::new(12, 13);
let p2 = Point::new(16, 16);

let mut poly = Polygon::new();


poly.add_point(p1);
poly.add_point(p2);
assert_eq!(poly.left_most_point(), Some(p1));
}

#[test]
fn test_polygon_iter() {
let p1 = Point::new(12, 13);
let p2 = Point::new(16, 16);

let mut poly = Polygon::new();


poly.add_point(p1);
poly.add_point(p2);

let points = poly.iter().cloned().collect::<Vec<_>>();


assert_eq!(points, vec![Point::new(12, 13), Point::new(16, 16)]);
}

#[test]
fn test_shape_perimeters() {
let mut poly = Polygon::new();
poly.add_point(Point::new(12, 13));
poly.add_point(Point::new(17, 11));
poly.add_point(Point::new(16, 16));
let shapes = vec![
Shape::from(poly),
Shape::from(Circle::new(Point::new(10, 20), 5)),
];
let perimeters = shapes
.iter()
.map(Shape::perimeter)
.map(round_two_digits)
.collect::<Vec<_>>();
assert_eq!(perimeters, vec![15.48, 31.42]);
}
}
This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
#[allow(dead_code)]
traffic.Learn more OK, got it
fn main() {}

89 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

90 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Control Flow
As we have seen, if is an expression in Rust. It is used to conditionally evaluate one of two
blocks, but the blocks can have a value which then becomes the value of the if expression.
Other control flow expressions work similarly in Rust.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

91 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Blocks
A block in Rust contains a sequence of expressions. Each block has a value and a type, which
are those of the last expression of the block:

1 fn main() {
2 let x = {
3 let y = 10;
4 println!("y: {y}");
5 let z = {
6 let w = {
7 3 + 4
8 };
9 println!("w: {w}");
10 y * w
11 };
12 println!("z: {z}");
13 z - y
14 };
15 println!("x: {x}");
16 }

If the last expression ends with ; , then the resulting value and type is () .

The same rule is used for functions: the value of the function body is the return value:

1 fn double(x: i32) -> i32 {


2 x + x
3 }
4
5 fn main() {
6 println!("doubled: {}", double(7));
7 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

92 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

if expressions
You use if expressions exactly like if statements in other languages:

1 fn main() {
2 let mut x = 10;
3 if x % 2 == 0 {
4 x = x / 2;
5 } else {
6 x = 3 * x + 1;
7 }
8 }

In addition, you can use if as an expression. The last expression of each block becomes
the value of the if expression:

1 fn main() {
2 let mut x = 10;
3 x = if x % 2 == 0 {
4 x / 2
5 } else {
6 3 * x + 1
7 };
8 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

93 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

if let expressions
The if let expression lets you execute different code depending on whether a value
matches a pattern:

1 fn main() {
2 let arg = std::env::args().next();
3 if let Some(value) = arg {
4 println!("Program name: {value}");
5 } else {
6 println!("Missing name?");
7 }
8 }

See pattern matching for more details on patterns in Rust.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

94 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

while loops
The while keyword works very similar to other languages:

1 fn main() {
2 let mut x = 10;
3 while x != 1 {
4 x = if x % 2 == 0 {
5 x / 2
6 } else {
7 3 * x + 1
8 };
9 }
10 println!("Final x: {x}");
11 }

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

95 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

while let loops


Like with if let , there is a while let variant which repeatedly tests a value against a
pattern:

1 fn main() {
2 let v = vec![10, 20, 30];
3 let mut iter = v.into_iter();
4
5 while let Some(x) = iter.next() {
6 println!("x: {x}");
7 }
8 }

Here the iterator returned by v.iter() will return a Option<i32> on every call to next() .
It returns Some(x) until it is done, after which it will return None . The while let lets us
keep iterating through all items.

See pattern matching for more details on patterns in Rust.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

96 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

for loops
The for loop is closely related to the while let loop. It will automatically call into_iter()
on the expression and then iterate over it:

1 fn main() {
2 let v = vec![10, 20, 30];
3
4 for x in v {
5 println!("x: {x}");
6 }
7
8 for i in (0..10).step_by(2) {
9 println!("i: {i}");
10 }
11 }

You can use break and continue here as usual.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

97 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

loop expressions
Finally, there is a loop keyword which creates an endless loop.

Here you must either break or return to stop the loop:

1 fn main() {
2 let mut x = 10;
3 loop {
4 x = if x % 2 == 0 {
5 x / 2
6 } else {
7 3 * x + 1
8 };
9 if x == 1 {
10 break;
11 }
12 }
13 println!("Final x: {x}");
14 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

98 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

match expressions
The match keyword is used to match a value against one or more patterns. In that sense, it
works like a series of if let expressions:

1 fn main() {
2 match std::env::args().next().as_deref() {
3 Some("cat") => println!("Will do cat things"),
4 Some("ls") => println!("Will ls some files"),
5 Some("mv") => println!("Let's move some files"),
6 Some("rm") => println!("Uh, dangerous!"),
7 None => println!("Hmm, no program name?"),
8 _ => println!("Unknown program name!"),
9 }
10 }

Like if let , each match arm must have the same type. The type is the last expression of
the block, if any. In the example above, the type is () .

See pattern matching for more details on patterns in Rust.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

99 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

break and continue


• If you want to exit a loop early, use break ,
• If you want to immediately start the next iteration use continue .

Both continue and break can optionally take a label argument which is used to break out
of nested loops:

1 fn main() {
2 let v = vec![10, 20, 30];
3 let mut iter = v.into_iter();
4 'outer: while let Some(x) = iter.next() {
5 println!("x: {x}");
6 let mut i = 0;
7 while i < x {
8 println!("x: {x}, i: {i}");
9 i += 1;
10 if i == 3 {
11 break 'outer;
12 }
13 }
14 }
15 }

In this case we break the outer loop after 3 iterations of the inner loop.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

100 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Standard Library
Rust comes with a standard library which helps establish a set of common types used by
Rust library and programs. This way, two libraries can work together smoothly because they
both use the same String type.

The common vocabulary types include:

• Option and Result types: used for optional values and error handling.

• String : the default string type used for owned data.

• Vec : a standard extensible vector.

• HashMap : a hash map type with a configurable hashing algorithm.

• Box : an owned pointer for heap-allocated data.

• Rc : a shared reference-counted pointer for heap-allocated data.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

101 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Option and Result


The types represent optional data:

1 fn main() {
2 let numbers = vec![10, 20, 30];
3 let first: Option<&i8> = numbers.first();
4 println!("first: {first:?}");
5
6 let idx: Result<usize, usize> = numbers.binary_search(&10);
7 println!("idx: {idx:?}");
8 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

102 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

String
String is the standard heap-allocated growable UTF-8 string buffer:

1 fn main() {
2 let mut s1 = String::new();
3 s1.push_str("Hello");
4 println!("s1: len = {}, capacity = {}", s1.len(), s1.capacity());
5
6 let mut s2 = String::with_capacity(s1.len() + 1);
7 s2.push_str(&s1);
8 s2.push('!');
9 println!("s2: len = {}, capacity = {}", s2.len(), s2.capacity());
10
11 let s3 = String::from(" � � ");
12 println!("s3: len = {}, number of chars = {}", s3.len(),
13 s3.chars().count());
14 }

String implements Deref<Target = str> , which means that you can call all str methods
on a String .

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

103 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Vec
Vec is the standard resizable heap-allocated buffer:

1 fn main() {
2 let mut v1 = Vec::new();
3 v1.push(42);
4 println!("v1: len = {}, capacity = {}", v1.len(), v1.capacity());
5
6 let mut v2 = Vec::with_capacity(v1.len() + 1);
7 v2.extend(v1.iter());
8 v2.push(9999);
9 println!("v2: len = {}, capacity = {}", v2.len(), v2.capacity());
10
11 // Canonical macro to initialize a vector with elements.
12 let mut v3 = vec![0, 0, 1, 2, 3, 4];
13
14 // Retain only the even elements.
15 v3.retain(|x| x % 2 == 0);
16 println!("{v3:?}");
17
18 // Remove consecutive duplicates.
19 v3.dedup();
20 println!("{v3:?}");
21 }

Vec implements Deref<Target = [T]> , which means that you can call slice methods on a
Vec .

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

104 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

HashMap
Standard hash map with protection against HashDoS attacks:

1 use std::collections::HashMap;
2
3 fn main() {
4 let mut page_counts = HashMap::new();
5 page_counts.insert("Adventures of Huckleberry Finn".to_string(), 207);
6 page_counts.insert("Grimms' Fairy Tales".to_string(), 751);
7 page_counts.insert("Pride and Prejudice".to_string(), 303);
8
9 if !page_counts.contains_key("Les Misérables") {
10 println!("We know about {} books, but not Les Misérables.",
11 page_counts.len());
12 }
13
14 for book in ["Pride and Prejudice", "Alice's Adventure in Wonderland"] {
15 match page_counts.get(book) {
16 Some(count) => println!("{book}: {count} pages"),
17 None => println!("{book} is unknown.")
18 }
19 }
20
21 // Use the .entry() method to insert a value if nothing is found.
22 for book in ["Pride and Prejudice", "Alice's Adventure in Wonderland"] {
23 let page_count: &mut i32 = page_counts.entry(book.to_string()).or_insert
24 *page_count += 1;
25 }
26
27 println!("{page_counts:#?}");
28 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

105 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Box
Box is an owned pointer to data on the heap:

1 fn main() {
2 let five = Box::new(5);
3 println!("five: {}", *five);
4 }

Stack Heap

five

Box<T> implements Deref<Target = T> , which means that you can call methods from T
directly on a Box<T> .

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

106 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Box with Recursive Data Structures


Recursive data types or data types with dynamic sizes need to use a Box :

1 #[derive(Debug)]
2 enum List<T> {
3 Cons(T, Box<List<T>>),
4 Nil,
5 }
6
7 fn main() {
8 let list: List<i32> = List::Cons(1, Box::new(List::Cons(2, Box::new(List::
9 println!("{list:?}");
10 }

Stack Heap

list

Cons 1 Cons 2 Nil

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

107 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Niche Optimization
1 #[derive(Debug)]
2 enum List<T> {
3 Cons(T, Box<List<T>>),
4 Nil,
5 }
6
7 fn main() {
8 let list: List<i32> = List::Cons(1, Box::new(List::Cons(2, Box::new(List::
9 println!("{list:?}");
10 }

A Box cannot be empty, so the pointer is always valid and non- null . This allows the
compiler to optimize the memory layout:

Stack Heap

list

1 2 null

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

108 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Rc
Rc is a reference-counted shared pointer. Use this when you need to refer to the same data
from multiple places:

1 use std::rc::Rc;
2
3 fn main() {
4 let mut a = Rc::new(10);
5 let mut b = Rc::clone(&a);
6
7 println!("a: {a}");
8 println!("b: {b}");
9 }

• If you need to mutate the data inside an Rc , you will need to wrap the data in a type
such as Cell or RefCell .
• See Arc and Mutex if you are in a multi-threaded context.
• You can downgrade a shared pointer into a Weak pointer to create cycles that will get
dropped.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

109 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Modules
We have seen how impl blocks let us namespace functions to a type.

Similarly, mod lets us namespace types and functions:

1 mod foo {
2 pub fn do_something() {
3 println!("In the foo module");
4 }
5 }
6
7 mod bar {
8 pub fn do_something() {
9 println!("In the bar module");
10 }
11 }
12
13 fn main() {
14 foo::do_something();
15 bar::do_something();
16 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

110 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Visibility
Modules are a privacy boundary:

• Module items are private by default (hides implementation details).


• Parent and sibling items are always visible.
• In other words, if an item is visible in module foo , it’s visible in all the descendants of
foo .

1 mod outer {
2 fn private() {
3 println!("outer::private");
4 }
5
6 pub fn public() {
7 println!("outer::public");
8 }
9
10 mod inner {
11 fn private() {
12 println!("outer::inner::private");
13 }
14
15 pub fn public() {
16 println!("outer::inner::public");
17 super::private();
18 }
19 }
20 }
21
22 fn main() {
23 outer::public();
24 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

111 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Paths
Paths are resolved as follows:

1. As a relative path:

◦ foo or self::foo refers to foo in the current module,


◦ super::foo refers to foo in the parent module.

2. As an absolute path:

◦ crate::foo refers to foo in the root of the current crate,


◦ bar::foo refers to foo in the bar crate.

A module can bring symbols from another module into scope with use . You will typically
see something like this at the top of each module:

1 use std::collections::HashSet;
2 use std::mem::transmute;

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

112 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Filesystem Hierarchy
Omitting the module content will tell Rust to look for it in another file:

1 mod garden;

This tells rust that the garden module content is found at src/garden.rs . Similarly, a
garden::vegetables module can be found at src/garden/vegetables.rs .

The crate root is in:

• src/lib.rs (for a library crate)


• src/main.rs (for a binary crate)

Modules defined in files can be documented, too, using “inner doc comments”. These
document the item that contains them – in this case, a module.

1 //! This module implements the garden, including a highly performant germination
2 //! implementation.
3
4 // Re-export types from this module.
5 pub use seeds::SeedPacket;
6 pub use garden::Garden;
7
8 /// Sow the given seed packets.
9 pub fn sow(seeds: Vec<SeedPacket>) { todo!() }
10
11 /// Harvest the produce in the garden that is ready.
12 pub fn harvest(garden: &mut Garden) { todo!() }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

113 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Day 2: Afternoon Exercises


The exercises for this afternoon will focus on strings and iterators.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

114 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Luhn Algorithm
The Luhn algorithm is used to validate credit card numbers. The algorithm takes a string as
input and does the following to validate the credit card number:

• Ignore all spaces. Reject number with less than two digits.

• Moving from right to left, double every second digit: for the number 1234 , we double
3 and 1 .

• After doubling a digit, sum the digits. So doubling 7 becomes 14 which becomes 5 .

• Sum all the undoubled and doubled digits.

• The credit card number is valid if the sum ends with 0 .

Copy the following code to https://play.rust-lang.org/ and implement the function:

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

115 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

// TODO: remove this when you're done with your implementation.


#![allow(unused_variables, dead_code)]

pub fn luhn(cc_number: &str) -> bool {


unimplemented!()
}

#[test]
fn test_non_digit_cc_number() {
assert!(!luhn("foo"));
}

#[test]
fn test_empty_cc_number() {
assert!(!luhn(""));
assert!(!luhn(" "));
assert!(!luhn(" "));
assert!(!luhn(" "));
}

#[test]
fn test_single_digit_cc_number() {
assert!(!luhn("0"));
}

#[test]
fn test_two_digit_cc_number() {
assert!(luhn(" 0 0 "));
}

#[test]
fn test_valid_cc_number() {
assert!(luhn("4263 9826 4026 9299"));
assert!(luhn("4539 3195 0343 6467"));
assert!(luhn("7992 7398 713"));
}

#[test]
fn test_invalid_cc_number() {
assert!(!luhn("4223 9826 4026 9299"));
assert!(!luhn("4539 3195 0343 6476"));
assert!(!luhn("8273 1232 7352 0569"));
}

#[allow(dead_code)]
fn main() {}

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

116 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Strings and Iterators


In this exercise, you are implementing a routing component of a web server. The server is
configured with a number of path prefixes which are matched against request paths. The path
prefixes can contain a wildcard character which matches a full segment. See the unit tests
below.

Copy the following code to https://play.rust-lang.org/ and make the tests pass. Try avoiding
allocating a Vec for your intermediate results:

// TODO: remove this when you're done with your implementation.


#![allow(unused_variables, dead_code)]

pub fn prefix_matches(prefix: &str, request_path: &str) -> bool {


unimplemented!()
}

#[test]
fn test_matches_without_wildcard() {
assert!(prefix_matches("/v1/publishers", "/v1/publishers"));
assert!(prefix_matches("/v1/publishers", "/v1/publishers/abc-123"));
assert!(prefix_matches("/v1/publishers", "/v1/publishers/abc/books"));

assert!(!prefix_matches("/v1/publishers", "/v1"));
assert!(!prefix_matches("/v1/publishers", "/v1/publishersBooks"));
assert!(!prefix_matches("/v1/publishers", "/v1/parent/publishers"));
}

#[test]
fn test_matches_with_wildcard() {
assert!(prefix_matches(
"/v1/publishers/*/books",
"/v1/publishers/foo/books"
));
assert!(prefix_matches(
"/v1/publishers/*/books",
"/v1/publishers/bar/books"
));
assert!(prefix_matches(
"/v1/publishers/*/books",
"/v1/publishers/foo/books/book1"
));

assert!(!prefix_matches("/v1/publishers/*/books", "/v1/publishers"));
assert!(!prefix_matches(
"/v1/publishers/*/books",
"/v1/publishers/foo/booksByAuthor"
This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
));
traffic.
} Learn more OK, got it

117 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Welcome to Day 3
Today, we will cover some more advanced topics of Rust:

• Traits: deriving traits, default methods, and important standard library traits.

• Generics: generic data types, generic methods, monomorphization, and trait objects.

• Error handling: panics, Result , and the try operator ? .

• Testing: unit tests, documentation tests, and integration tests.

• Unsafe Rust: raw pointers, static variables, unsafe functions, and extern functions.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

118 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Generics
Rust support generics, which lets you abstract algorithms or data structures (such as sorting
or a binary tree) over the types used or stored.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

119 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Generic Data Types


You can use generics to abstract over the concrete field type:

1 #[derive(Debug)]
2 struct Point<T> {
3 x: T,
4 y: T,
5 }
6
7 fn main() {
8 let integer = Point { x: 5, y: 10 };
9 let float = Point { x: 1.0, y: 4.0 };
10 println!("{integer:?} and {float:?}");
11 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

120 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Generic Methods
You can declare a generic type on your impl block:

1 #[derive(Debug)]
2 struct Point<T>(T, T);
3
4 impl<T> Point<T> {
5 fn x(&self) -> &T {
6 &self.0 // + 10
7 }
8
9 // fn set_x(&mut self, x: T)
10 }
11
12 fn main() {
13 let p = Point(5, 10);
14 println!("p.x = {}", p.x());
15 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

121 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Monomorphization
Generic code is turned into non-generic code based on the call sites:

1 fn main() {
2 let integer = Some(5);
3 let float = Some(5.0);
4 }

behaves as if you wrote

1 enum Option_i32 {
2 Some(i32),
3 None,
4 }
5
6 enum Option_f64 {
7 Some(f64),
8 None,
9 }
10
11 fn main() {
12 let integer = Option_i32::Some(5);
13 let float = Option_f64::Some(5.0);
14 }

This is a zero-cost abstraction: you get exactly the same result as if you had hand-coded the
data structures without the abstraction.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

122 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Traits
Rust lets you abstract over types with traits. They’re similar to interfaces:

1 trait Pet {
2 fn name(&self) -> String;
3 }
4
5 struct Dog {
6 name: String,
7 }
8
9 struct Cat;
10
11 impl Pet for Dog {
12 fn name(&self) -> String {
13 self.name.clone()
14 }
15 }
16
17 impl Pet for Cat {
18 fn name(&self) -> String {
19 String::from("The cat") // No name, cats won't respond to it anyway.
20 }
21 }
22
23 fn greet<P: Pet>(pet: &P) {
24 println!("Who's a cutie? {} is!", pet.name());
25 }
26
27 fn main() {
28 let fido = Dog { name: "Fido".into() };
29 greet(&fido);
30
31 let captain_floof = Cat;
32 greet(&captain_floof);
33 }

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

123 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Trait Objects
Trait objects allow for values of different types, for instance in a collection:

1 trait Pet {
2 fn name(&self) -> String;
3 }
4
5 struct Dog {
6 name: String,
7 }
8
9 struct Cat;
10
11 impl Pet for Dog {
12 fn name(&self) -> String {
13 self.name.clone()
14 }
15 }
16
17 impl Pet for Cat {
18 fn name(&self) -> String {
19 String::from("The cat") // No name, cats won't respond to it anyway.
20 }
21 }
22
23 fn main() {
24 let pets: Vec<Box<dyn Pet>> = vec![
25 Box::new(Cat),
26 Box::new(Dog { name: String::from("Fido") }),
27 ];
28 for pet in pets {
29 println!("Hello {}!", pet.name());
30 }
31 }

Memory layout after allocating pets :

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

124 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Stack Heap

pets

ptr
len 2
capacity 2
name: Fido

<Dog as Pet>::name

<Cat as Pet>::name

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

125 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Deriving Traits
Rust derive macros work by automatically generating code that implements the specified
traits for a data structure.

You can let the compiler derive a number of traits as follows:

1 #[derive(Debug, Clone, PartialEq, Eq, Default)]


2 struct Player {
3 name: String,
4 strength: u8,
5 hit_points: u8,
6 }
7
8 fn main() {
9 let p1 = Player::default();
10 let p2 = p1.clone();
11 println!("Is {:?}\nequal to {:?}?\nThe answer is {}!", &p1, &p2,
12 if p1 == p2 { "yes" } else { "no" });
13 }

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

126 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Default Methods
Traits can implement behavior in terms of other trait methods:

1 trait Equals {
2 fn equals(&self, other: &Self) -> bool;
3 fn not_equals(&self, other: &Self) -> bool {
4 !self.equals(other)
5 }
6 }
7
8 #[derive(Debug)]
9 struct Centimeter(i16);
10
11 impl Equals for Centimeter {
12 fn equals(&self, other: &Centimeter) -> bool {
13 self.0 == other.0
14 }
15 }
16
17 fn main() {
18 let a = Centimeter(10);
19 let b = Centimeter(20);
20 println!("{a:?} equals {b:?}: {}", a.equals(&b));
21 println!("{a:?} not_equals {b:?}: {}", a.not_equals(&b));
22 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

127 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Trait Bounds
When working with generics, you often want to require the types to implement some trait,
so that you can call this trait’s methods.

You can do this with T: Trait or impl Trait :

1 fn duplicate<T: Clone>(a: T) -> (T, T) {


2 (a.clone(), a.clone())
3 }
4
5 // Syntactic sugar for:
6 // fn add_42_millions<T: Into<i32>>(x: T) -> i32 {
7 fn add_42_millions(x: impl Into<i32>) -> i32 {
8 x.into() + 42_000_000
9 }
10
11 // struct NotClonable;
12
13 fn main() {
14 let foo = String::from("foo");
15 let pair = duplicate(foo);
16 println!("{pair:?}");
17
18 let many = add_42_millions(42_i8);
19 println!("{many}");
20 let many_more = add_42_millions(10_000_000);
21 println!("{many_more}");
22 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

128 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

impl Trait
Similar to trait bounds, an impl Trait syntax can be used in function arguments and return
values:

1 use std::fmt::Display;
2
3 fn get_x(name: impl Display) -> impl Display {
4 format!("Hello {name}")
5 }
6
7 fn main() {
8 let x = get_x("foo");
9 println!("{x}");
10 }

• impl Trait allows you to work with types which you cannot name.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

129 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Important Traits
We will now look at some of the most common traits of the Rust standard library:

• Iterator and IntoIterator used in for loops,


• From and Into used to convert values,
• Read and Write used for IO,
• Add , Mul , … used for operator overloading, and
• Drop used for defining destructors.
• Default used to construct a default instance of a type.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

130 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Iterators
You can implement the Iterator trait on your own types:

1 struct Fibonacci {
2 curr: u32,
3 next: u32,
4 }
5
6 impl Iterator for Fibonacci {
7 type Item = u32;
8
9 fn next(&mut self) -> Option<Self::Item> {
10 let new_next = self.curr + self.next;
11 self.curr = self.next;
12 self.next = new_next;
13 Some(self.curr)
14 }
15 }
16
17 fn main() {
18 let fib = Fibonacci { curr: 0, next: 1 };
19 for (i, n) in fib.enumerate().take(5) {
20 println!("fib({i}): {n}");
21 }
22 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

131 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

FromIterator
FromIterator lets you build a collection from an Iterator .

1 fn main() {
2 let primes = vec![2, 3, 5, 7];
3 let prime_squares = primes
4 .into_iter()
5 .map(|prime| prime * prime)
6 .collect::<Vec<_>>();
7 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

132 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

From and Into


Types implement From and Into to facilitate type conversions:

1 fn main() {
2 let s = String::from("hello");
3 let addr = std::net::Ipv4Addr::from([127, 0, 0, 1]);
4 let one = i16::from(true);
5 let bigger = i32::from(123i16);
6 println!("{s}, {addr}, {one}, {bigger}");
7 }

Into is automatically implemented when From is implemented:

1 fn main() {
2 let s: String = "hello".into();
3 let addr: std::net::Ipv4Addr = [127, 0, 0, 1].into();
4 let one: i16 = true.into();
5 let bigger: i32 = 123i16.into();
6 println!("{s}, {addr}, {one}, {bigger}");
7 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

133 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Read and Write


Using Read and BufRead , you can abstract over u8 sources:

1 use std::io::{BufRead, BufReader, Read, Result};


2
3 fn count_lines<R: Read>(reader: R) -> usize {
4 let buf_reader = BufReader::new(reader);
5 buf_reader.lines().count()
6 }
7
8 fn main() -> Result<()> {
9 let slice: &[u8] = b"foo\nbar\nbaz\n";
10 println!("lines in slice: {}", count_lines(slice));
11
12 let file = std::fs::File::open(std::env::current_exe()?)?;
13 println!("lines in file: {}", count_lines(file));
14 Ok(())
15 }

Similarly, Write lets you abstract over u8 sinks:

1 use std::io::{Result, Write};


2
3 fn log<W: Write>(writer: &mut W, msg: &str) -> Result<()> {
4 writer.write_all(msg.as_bytes())?;
5 writer.write_all("\n".as_bytes())
6 }
7
8 fn main() -> Result<()> {
9 let mut buffer = Vec::new();
10 log(&mut buffer, "Hello")?;
11 log(&mut buffer, "World")?;
12 println!("Logged: {:?}", buffer);
13 Ok(())
14 }

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

134 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

The Drop Trait


Values which implement Drop can specify code to run when they go out of scope:

1 struct Droppable {
2 name: &'static str,
3 }
4
5 impl Drop for Droppable {
6 fn drop(&mut self) {
7 println!("Dropping {}", self.name);
8 }
9 }
10
11 fn main() {
12 let a = Droppable { name: "a" };
13 {
14 let b = Droppable { name: "b" };
15 {
16 let c = Droppable { name: "c" };
17 let d = Droppable { name: "d" };
18 println!("Exiting block B");
19 }
20 println!("Exiting block A");
21 }
22 drop(a);
23 println!("Exiting main");
24 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

135 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

The Default Trait


Default trait produces a default value for a type.

1 #[derive(Debug, Default)]
2 struct Derived {
3 x: u32,
4 y: String,
5 z: Implemented,
6 }
7
8 #[derive(Debug)]
9 struct Implemented(String);
10
11 impl Default for Implemented {
12 fn default() -> Self {
13 Self("John Smith".into())
14 }
15 }
16
17 fn main() {
18 let default_struct: Derived = Default::default();
19 println!("{default_struct:#?}");
20
21 let almost_default_struct = Derived {
22 y: "Y is set!".into(),
23 ..Default::default()
24 };
25 println!("{almost_default_struct:#?}");
26
27 let nothing: Option<Derived> = None;
28 println!("{:#?}", nothing.unwrap_or_default());
29 }
30

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

136 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Add, Mul, …
Operator overloading is implemented via traits in std::ops :

1 #[derive(Debug, Copy, Clone)]


2 struct Point { x: i32, y: i32 }
3
4 impl std::ops::Add for Point {
5 type Output = Self;
6
7 fn add(self, other: Self) -> Self {
8 Self {x: self.x + other.x, y: self.y + other.y}
9 }
10 }
11
12 fn main() {
13 let p1 = Point { x: 10, y: 20 };
14 let p2 = Point { x: 100, y: 200 };
15 println!("{:?} + {:?} = {:?}", p1, p2, p1 + p2);
16 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

137 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Closures
Closures or lambda expressions have types which cannot be named. However, they
implement special Fn , FnMut , and FnOnce traits:

1 fn apply_with_log(func: impl FnOnce(i32) -> i32, input: i32) -> i32 {


2 println!("Calling function on {input}");
3 func(input)
4 }
5
6 fn main() {
7 let add_3 = |x| x + 3;
8 println!("add_3: {}", apply_with_log(add_3, 10));
9 println!("add_3: {}", apply_with_log(add_3, 20));
10
11 let mut v = Vec::new();
12 let mut accumulate = |x: i32| {
13 v.push(x);
14 v.iter().sum::<i32>()
15 };
16 println!("accumulate: {}", apply_with_log(&mut accumulate, 4));
17 println!("accumulate: {}", apply_with_log(&mut accumulate, 5));
18
19 let multiply_sum = |x| x * v.into_iter().sum::<i32>();
20 println!("multiply_sum: {}", apply_with_log(multiply_sum, 3));
21 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

138 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Day 3: Morning Exercises


We will design a classical GUI library traits and trait objects.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

139 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

A Simple GUI Library


Let us design a classical GUI library using our new knowledge of traits and trait objects.

We will have a number of widgets in our library:

• Window : has a title and contains other widgets.


• Button : has a label and a callback function which is invoked when the button is
pressed.
• Label : has a label .

The widgets will implement a Widget trait, see below.

Copy the code below to https://play.rust-lang.org/, fill in the missing draw_into methods so
that you implement the Widget trait:

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

140 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

// TODO: remove this when you're done with your implementation.


#![allow(unused_imports, unused_variables, dead_code)]

pub trait Widget {


/// Natural width of `self`.
fn width(&self) -> usize;

/// Draw the widget into a buffer.


fn draw_into(&self, buffer: &mut dyn std::fmt::Write);

/// Draw the widget on standard output.


fn draw(&self) {
let mut buffer = String::new();
self.draw_into(&mut buffer);
println!("{buffer}");
}
}

pub struct Label {


label: String,
}

impl Label {
fn new(label: &str) -> Label {
Label {
label: label.to_owned(),
}
}
}

pub struct Button {


label: Label,
callback: Box<dyn FnMut()>,
}

impl Button {
fn new(label: &str, callback: Box<dyn FnMut()>) -> Button {
Button {
label: Label::new(label),
callback,
}
}
}

pub struct Window {


title: String,
widgets: Vec<Box<dyn Widget>>,
}

impl Window {
This site
fnuses cookies from
new(title: Google
&str) ->to Window
deliver and
{ enhance the quality of its services and to analyze
traffic. Window
Learn more { OK, got it

141 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

title: title.to_owned(),
widgets: Vec::new(),
}
}

fn add_widget(&mut self, widget: Box<dyn Widget>) {


self.widgets.push(widget);
}

fn inner_width(&self) -> usize {


std::cmp::max(
self.title.chars().count(),
self.widgets.iter().map(|w| w.width()).max().unwrap_or(0),
)
}
}

impl Widget for Label {


fn width(&self) -> usize {
unimplemented!()
}

fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {


unimplemented!()
}
}

impl Widget for Button {


fn width(&self) -> usize {
unimplemented!()
}

fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {


unimplemented!()
}
}

impl Widget for Window {


fn width(&self) -> usize {
unimplemented!()
}

fn draw_into(&self, buffer: &mut dyn std::fmt::Write) {


unimplemented!()
}
}

fn main() {
let mut window = Window::new("Rust GUI Demo 1.23");
window.add_widget(Box::new(Label::new("This
This site is of
uses cookies from Google to deliver and enhance the quality a its
small text
services andGUI demo.")));
to analyze
window.add_widget(Box::new(Button::new(
traffic. Learn more OK, got it
"Click me!",

142 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Box::new(|| println!("You clicked the button!")),


)));
window.draw();
}

The output of the above program can be something simple like this:

========
Rust GUI Demo 1.23
========

This is a small text GUI demo.

| Click me! |

If you want to draw aligned text, you can use the fill/alignment formatting operators. In
particular, notice how you can pad with different characters (here a '/' ) and how you can
control alignment:

1 fn main() {
2 let width = 10;
3 println!("left aligned: |{:/<width$}|", "foo");
4 println!("centered: |{:/^width$}|", "foo");
5 println!("right aligned: |{:/>width$}|", "foo");
6 }

Using such alignment tricks, you can for example produce output like this:

+--------------------------------+
| Rust GUI Demo 1.23 |
+================================+
| This is a small text GUI demo. |
| +-----------+ |
| | Click me! | |
| +-----------+ |
+--------------------------------+

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

143 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Error Handling
Error handling in Rust is done using explicit control flow:

• Functions that can have errors list this in their return type,
• There are no exceptions.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

144 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Panics
Rust will trigger a panic if a fatal error happens at runtime:

1 fn main() {
2 let v = vec![10, 20, 30];
3 println!("v[100]: {}", v[100]);
4 }

• Panics are for unrecoverable and unexpected errors.


◦ Panics are symptoms of bugs in the program.
• Use non-panicking APIs (such as Vec::get ) if crashing is not acceptable.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

145 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Catching the Stack Unwinding


By default, a panic will cause the stack to unwind. The unwinding can be caught:

1 use std::panic;
2
3 fn main() {
4 let result = panic::catch_unwind(|| {
5 println!("hello!");
6 });
7 assert!(result.is_ok());
8
9 let result = panic::catch_unwind(|| {
10 panic!("oh no!");
11 });
12 assert!(result.is_err());
13 }

• This can be useful in servers which should keep running even if a single request
crashes.
• This does not work if panic = 'abort' is set in your Cargo.toml .

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

146 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Structured Error Handling with Result


We have already seen the Result enum. This is used pervasively when errors are expected
as part of normal operation:

1 use std::fs;
2 use std::io::Read;
3
4 fn main() {
5 let file = fs::File::open("diary.txt");
6 match file {
7 Ok(mut file) => {
8 let mut contents = String::new();
9 file.read_to_string(&mut contents);
10 println!("Dear diary: {contents}");
11 },
12 Err(err) => {
13 println!("The diary could not be opened: {err}");
14 }
15 }
16 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

147 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Propagating Errors with ?


The try-operator ? is used to return errors to the caller. It lets you turn the common

match some_expression {
Ok(value) => value,
Err(err) => return Err(err),
}

into the much simpler

some_expression?

We can use this to simplify our error handling code:

1 use std::{fs, io};


2 use std::io::Read;
3
4 fn read_username(path: &str) -> Result<String, io::Error> {
5 let username_file_result = fs::File::open(path);
6 let mut username_file = match username_file_result {
7 Ok(file) => file,
8 Err(err) => return Err(err),
9 };
10
11 let mut username = String::new();
12 match username_file.read_to_string(&mut username) {
13 Ok(_) => Ok(username),
14 Err(err) => Err(err),
15 }
16 }
17
18 fn main() {
19 //fs::write("config.dat", "alice").unwrap();
20 let username = read_username("config.dat");
21 println!("username or error: {username:?}");
22 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

148 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Converting Error Types


The effective expansion of ? is a little more complicated than previously indicated:

expression?

works the same as

match expression {
Ok(value) => value,
Err(err) => return Err(From::from(err)),
}

The From::from call here means we attempt to convert the error type to the type returned
by the function:

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

149 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Converting Error Types


1 use std::error::Error;
2 use std::fmt::{self, Display, Formatter};
3 use std::fs::{self, File};
4 use std::io::{self, Read};
5
6 #[derive(Debug)]
7 enum ReadUsernameError {
8 IoError(io::Error),
9 EmptyUsername(String),
10 }
11
12 impl Error for ReadUsernameError {}
13
14 impl Display for ReadUsernameError {
15 fn fmt(&self, f: &mut Formatter) -> fmt::Result {
16 match self {
17 Self::IoError(e) => write!(f, "IO error: {e}"),
18 Self::EmptyUsername(filename) => write!(f, "Found no username in {filena
19 }
20 }
21 }
22
23 impl From<io::Error> for ReadUsernameError {
24 fn from(err: io::Error) -> ReadUsernameError {
25 ReadUsernameError::IoError(err)
26 }
27 }
28
29 fn read_username(path: &str) -> Result<String, ReadUsernameError> {
30 let mut username = String::with_capacity(100);
31 File::open(path)?.read_to_string(&mut username)?;
32 if username.is_empty() {
33 return Err(ReadUsernameError::EmptyUsername(String::from(path)));
34 }
35 Ok(username)
36 }
37
38 fn main() {
39 //fs::write("config.dat", "").unwrap();
40 let username = read_username("config.dat");
41 println!("username or error: {username:?}");
42 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

150 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Deriving Error Enums


The thiserror crate is a popular way to create an error enum like we did on the previous
page:

1 use std::{fs, io};


2 use std::io::Read;
3 use thiserror::Error;
4
5 #[derive(Debug, Error)]
6 enum ReadUsernameError {
7 #[error("Could not read: {0}")]
8 IoError(#[from] io::Error),
9 #[error("Found no username in {0}")]
10 EmptyUsername(String),
11 }
12
13 fn read_username(path: &str) -> Result<String, ReadUsernameError> {
14 let mut username = String::new();
15 fs::File::open(path)?.read_to_string(&mut username)?;
16 if username.is_empty() {
17 return Err(ReadUsernameError::EmptyUsername(String::from(path)));
18 }
19 Ok(username)
20 }
21
22 fn main() {
23 //fs::write("config.dat", "").unwrap();
24 match read_username("config.dat") {
25 Ok(username) => println!("Username: {username}"),
26 Err(err) => println!("Error: {err}"),
27 }
28 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

151 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Dynamic Error Types


Sometimes we want to allow any type of error to be returned without writing our own enum
covering all the different possibilities. std::error::Error makes this easy.

1 use std::fs;
2 use std::io::Read;
3 use thiserror::Error;
4 use std::error::Error;
5
6 #[derive(Clone, Debug, Eq, Error, PartialEq)]
7 #[error("Found no username in {0}")]
8 struct EmptyUsernameError(String);
9
10 fn read_username(path: &str) -> Result<String, Box<dyn Error>> {
11 let mut username = String::new();
12 fs::File::open(path)?.read_to_string(&mut username)?;
13 if username.is_empty() {
14 return Err(EmptyUsernameError(String::from(path)).into());
15 }
16 Ok(username)
17 }
18
19 fn main() {
20 //fs::write("config.dat", "").unwrap();
21 match read_username("config.dat") {
22 Ok(username) => println!("Username: {username}"),
23 Err(err) => println!("Error: {err}"),
24 }
25 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

152 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Adding Context to Errors


The widely used anyhow crate can help you add contextual information to your errors and
allows you to have fewer custom error types:

1 use std::{fs, io};


2 use std::io::Read;
3 use anyhow::{Context, Result, bail};
4
5 fn read_username(path: &str) -> Result<String> {
6 let mut username = String::with_capacity(100);
7 fs::File::open(path)
8 .with_context(|| format!("Failed to open {path}"))?
9 .read_to_string(&mut username)
10 .context("Failed to read")?;
11 if username.is_empty() {
12 bail!("Found no username in {path}");
13 }
14 Ok(username)
15 }
16
17 fn main() {
18 //fs::write("config.dat", "").unwrap();
19 match read_username("config.dat") {
20 Ok(username) => println!("Username: {username}"),
21 Err(err) => println!("Error: {err:?}"),
22 }
23 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

153 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Testing
Rust and Cargo come with a simple unit test framework:

• Unit tests are supported throughout your code.

• Integration tests are supported via the tests/ directory.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

154 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Unit Tests
Mark unit tests with #[test] :

1 fn first_word(text: &str) -> &str {


2 match text.find(' ') {
3 Some(idx) => &text[..idx],
4 None => &text,
5 }
6 }
7
8 #[test]
9 fn test_empty() {
10 assert_eq!(first_word(""), "");
11 }
12
13 #[test]
14 fn test_single_word() {
15 assert_eq!(first_word("Hello"), "Hello");
16 }
17
18 #[test]
19 fn test_multiple_words() {
20 assert_eq!(first_word("Hello World"), "Hello");
21 }

Use cargo test to find and run the unit tests.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

155 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Test Modules
Unit tests are often put in a nested module (run tests on the Playground):

1 fn helper(a: &str, b: &str) -> String {


2 format!("{a} {b}")
3 }
4
5 pub fn main() {
6 println!("{}", helper("Hello", "World"));
7 }
8
9 #[cfg(test)]
10 mod tests {
11 use super::*;
12
13 #[test]
14 fn test_helper() {
15 assert_eq!(helper("foo", "bar"), "foo bar");
16 }
17 }

• This lets you unit test private helpers.


• The #[cfg(test)] attribute is only active when you run cargo test .

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

156 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Documentation Tests
Rust has built-in support for documentation tests:

/// Shortens a string to the given length.


///
/// ```
/// use playground::shorten_string;
/// assert_eq!(shorten_string("Hello World", 5), "Hello");
/// assert_eq!(shorten_string("Hello World", 20), "Hello World");
/// ```
pub fn shorten_string(s: &str, length: usize) -> &str {
&s[..std::cmp::min(length, s.len())]
}

• Code blocks in /// comments are automatically seen as Rust code.


• The code will be compiled and executed as part of cargo test .
• Test the above code on the Rust Playground.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

157 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Integration Tests
If you want to test your library as a client, use an integration test.

Create a .rs file under tests/ :

use my_library::init;

#[test]
fn test_init() {
assert!(init().is_ok());
}

These tests only have access to the public API of your crate.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

158 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Useful crates for writing tests


Rust comes with only basic support for writing tests.

Here are some additional crates which we recommend for writing tests:

• googletest: Comprehensive test assertion library in the tradition of GoogleTest for C++.
• proptest: Property-based testing for Rust.
• rstest: Support for fixtures and parameterised tests.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

159 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Unsafe Rust
The Rust language has two parts:

• Safe Rust: memory safe, no undefined behavior possible.


• Unsafe Rust: can trigger undefined behavior if preconditions are violated.

We will be seeing mostly safe Rust in this course, but it’s important to know what Unsafe
Rust is.

Unsafe code is usually small and isolated, and its correctness should be carefully
documented. It is usually wrapped in a safe abstraction layer.

Unsafe Rust gives you access to five new capabilities:

• Dereference raw pointers.


• Access or modify mutable static variables.
• Access union fields.
• Call unsafe functions, including extern functions.
• Implement unsafe traits.

We will briefly cover unsafe capabilities next. For full details, please see Chapter 19.1 in the
Rust Book and the Rustonomicon.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

160 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Dereferencing Raw Pointers


Creating pointers is safe, but dereferencing them requires unsafe :

1 fn main() {
2 let mut num = 5;
3
4 let r1 = &mut num as *mut i32;
5 let r2 = r1 as *const i32;
6
7 // Safe because r1 and r2 were obtained from references and so are
8 // guaranteed to be non-null and properly aligned, the objects underlying
9 // the references from which they were obtained are live throughout the
10 // whole unsafe block, and they are not accessed either through the
11 // references or concurrently through any other pointers.
12 unsafe {
13 println!("r1 is: {}", *r1);
14 *r1 = 10;
15 println!("r2 is: {}", *r2);
16 }
17 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

161 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Mutable Static Variables


It is safe to read an immutable static variable:

1 static HELLO_WORLD: &str = "Hello, world!";


2
3 fn main() {
4 println!("HELLO_WORLD: {HELLO_WORLD}");
5 }

However, since data races can occur, it is unsafe to read and write mutable static variables:

1 static mut COUNTER: u32 = 0;


2
3 fn add_to_counter(inc: u32) {
4 unsafe { COUNTER += inc; } // Potential data race!
5 }
6
7 fn main() {
8 add_to_counter(42);
9
10 unsafe { println!("COUNTER: {COUNTER}"); } // Potential data race!
11 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

162 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Unions
Unions are like enums, but you need to track the active field yourself:

1 #[repr(C)]
2 union MyUnion {
3 i: u8,
4 b: bool,
5 }
6
7 fn main() {
8 let u = MyUnion { i: 42 };
9 println!("int: {}", unsafe { u.i });
10 println!("bool: {}", unsafe { u.b }); // Undefined behavior!
11 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

163 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Calling Unsafe Functions


A function or method can be marked unsafe if it has extra preconditions you must uphold
to avoid undefined behaviour:

1 fn main() {
2 �∈�
let emojis = "� �";
3
4 // Safe because the indices are in the correct order, within the bounds of
5 // the string slice, and lie on UTF-8 sequence boundaries.
6 unsafe {
7 println!("emoji: {}", emojis.get_unchecked(0..4));
8 println!("emoji: {}", emojis.get_unchecked(4..7));
9 println!("emoji: {}", emojis.get_unchecked(7..11));
10 }
11
12 println!("char count: {}", count_chars(unsafe { emojis.get_unchecked(0..
13
14 // Not upholding the UTF-8 encoding requirement breaks memory safety!
15 // println!("emoji: {}", unsafe { emojis.get_unchecked(0..3) });
16 // println!("char count: {}", count_chars(unsafe { emojis.get_unchecked(0..3) })
17 }
18
19 fn count_chars(s: &str) -> usize {
20 s.chars().map(|_| 1).sum()
21 }

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

164 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Writing Unsafe Functions


You can mark your own functions as unsafe if they require particular conditions to avoid
undefined behaviour.

1 /// Swaps the values pointed to by the given pointers.


2 ///
3 /// # Safety
4 ///
5 /// The pointers must be valid and properly aligned.
6 unsafe fn swap(a: *mut u8, b: *mut u8) {
7 let temp = *a;
8 *a = *b;
9 *b = temp;
10 }
11
12 fn main() {
13 let mut a = 42;
14 let mut b = 66;
15
16 // Safe because ...
17 unsafe {
18 swap(&mut a, &mut b);
19 }
20
21 println!("a = {}, b = {}", a, b);
22 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

165 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Calling External Code


Functions from other languages might violate the guarantees of Rust. Calling them is thus
unsafe:

1 extern "C" {
2 fn abs(input: i32) -> i32;
3 }
4
5 fn main() {
6 unsafe {
7 // Undefined behavior if abs misbehaves.
8 println!("Absolute value of -3 according to C: {}", abs(-3));
9 }
10 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

166 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Implementing Unsafe Traits


Like with functions, you can mark a trait as unsafe if the implementation must guarantee
particular conditions to avoid undefined behaviour.

For example, the zerocopy crate has an unsafe trait that looks something like this:

1 use std::mem::size_of_val;
2 use std::slice;
3
4 /// ...
5 /// # Safety
6 /// The type must have a defined representation and no padding.
7 pub unsafe trait AsBytes {
8 fn as_bytes(&self) -> &[u8] {
9 unsafe {
10 slice::from_raw_parts(self as *const Self as *const u8, size_of_val
11 }
12 }
13 }
14
15 // Safe because u32 has a defined representation and no padding.
16 unsafe impl AsBytes for u32 {}

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

167 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Day 3: Afternoon Exercises


Let us build a safe wrapper for reading directory content!

For this exercise, we suggest using a local dev environment instead of the Playground. This
will allow you to run your binary on your own machine.

To get started, follow the running locally instructions.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

168 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Safe FFI Wrapper


Rust has great support for calling functions through a foreign function interface (FFI). We will
use this to build a safe wrapper for the libc functions you would use from C to read the
filenames of a directory.

You will want to consult the manual pages:

• opendir(3)
• readdir(3)
• closedir(3)

You will also want to browse the std::ffi module. There you find a number of string types
which you need for the exercise:

Types Encoding Use


str and String UTF-8 Text processing in Rust
CStr and CString NUL-terminated Communicating with C functions
OsStr and OsString OS-specific Communicating with the OS

You will convert between all these types:

• &str to CString : you need to allocate space for a trailing \0 character,


• CString to *const i8 : you need a pointer to call C functions,
• *const i8 to &CStr : you need something which can find the trailing \0 character,
• &CStr to &[u8] : a slice of bytes is the universal interface for “some unknow data”,
• &[u8] to &OsStr : &OsStr is a step towards OsString , use OsStrExt to create it,
• &OsStr to OsString : you need to clone the data in &OsStr to be able to return it and
call readdir again.

The Nomicon also has a very useful chapter about FFI.

Copy the code below to https://play.rust-lang.org/ and fill in the missing functions and
methods:

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

169 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

// TODO: remove this when you're done with your implementation.


#![allow(unused_imports, unused_variables, dead_code)]

mod ffi {
use std::os::raw::{c_char, c_int};
#[cfg(not(target_os = "macos"))]
use std::os::raw::{c_long, c_ulong, c_ushort, c_uchar};

// Opaque type. See https://doc.rust-lang.org/nomicon/ffi.html.


#[repr(C)]
pub struct DIR {
_data: [u8; 0],
_marker: core::marker::PhantomData<(*mut u8,
core::marker::PhantomPinned)>,
}

// Layout according to the Linux man page for readdir(3), where ino_t and
// off_t are resolved according to the definitions in
// /usr/include/x86_64-linux-gnu/{sys/types.h, bits/typesizes.h}.
#[cfg(not(target_os = "macos"))]
#[repr(C)]
pub struct dirent {
pub d_ino: c_ulong,
pub d_off: c_long,
pub d_reclen: c_ushort,
pub d_type: c_uchar,
pub d_name: [c_char; 256],
}

// Layout according to the macOS man page for dir(5).


#[cfg(all(target_os = "macos"))]
#[repr(C)]
pub struct dirent {
pub d_fileno: u64,
pub d_seekoff: u64,
pub d_reclen: u16,
pub d_namlen: u16,
pub d_type: u8,
pub d_name: [c_char; 1024],
}

extern "C" {
pub fn opendir(s: *const c_char) -> *mut DIR;

#[cfg(not(all(target_os = "macos", target_arch = "x86_64")))]


pub fn readdir(s: *mut DIR) -> *const dirent;

// See https://github.com/rust-lang/libc/issues/414 and the section on


// _DARWIN_FEATURE_64_BIT_INODE in the macOS man page for stat(2).
//
This site uses
//cookies from Google
"Platforms thatto existed
deliver andbefore
enhancethese
the quality of its services
updates and to analyzerefers
were available"
// more
traffic. Learn to macOSOK, (as
got it opposed to iOS / wearOS / etc.) on Intel and PowerPC.

170 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

#[cfg(all(target_os = "macos", target_arch = "x86_64"))]


#[link_name = "readdir$INODE64"]
pub fn readdir(s: *mut DIR) -> *const dirent;

pub fn closedir(s: *mut DIR) -> c_int;


}
}

use std::ffi::{CStr, CString, OsStr, OsString};


use std::os::unix::ffi::OsStrExt;

#[derive(Debug)]
struct DirectoryIterator {
path: CString,
dir: *mut ffi::DIR,
}

impl DirectoryIterator {
fn new(path: &str) -> Result<DirectoryIterator, String> {
// Call opendir and return a Ok value if that worked,
// otherwise return Err with a message.
unimplemented!()
}
}

impl Iterator for DirectoryIterator {


type Item = OsString;
fn next(&mut self) -> Option<OsString> {
// Keep calling readdir until we get a NULL pointer back.
unimplemented!()
}
}

impl Drop for DirectoryIterator {


fn drop(&mut self) {
// Call closedir as needed.
unimplemented!()
}
}

fn main() -> Result<(), String> {


let iter = DirectoryIterator::new(".")?;
println!("files: {:#?}", iter.collect::<Vec<_>>());
Ok(())
}

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

171 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Welcome to Rust in Android


Rust is supported for native platform development on Android. This means that you can
write new operating system services in Rust, as well as extending existing services.

We will attempt to call Rust from one of your own projects today. So try to find a little
corner of your code base where we can move some lines of code to Rust. The fewer
dependencies and “exotic” types the better. Something that parses some raw bytes
would be ideal.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

172 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Setup
We will be using an Android Virtual Device to test our code. Make sure you have access to
one or create a new one with:

source build/envsetup.sh
lunch aosp_cf_x86_64_phone-userdebug
acloud create

Please see the Android Developer Codelab for details.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

173 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Build Rules
The Android build system (Soong) supports Rust via a number of modules:

Module Type Description


rust_binary Produces a Rust binary.
Produces a Rust library, and provides both rlib and dylib
rust_library
variants.
Produces a Rust C library usable by cc modules, and provides
rust_ffi
both static and shared variants.
Produces a proc-macro Rust library. These are analogous to
rust_proc_macro
compiler plugins.
Produces a Rust test binary that uses the standard Rust test
rust_test
harness.
rust_fuzz Produces a Rust fuzz binary leveraging libfuzzer .
Generates source and produces a Rust library that provides an
rust_protobuf
interface for a particular protobuf.
Generates source and produces a Rust library containing Rust
rust_bindgen
bindings to C libraries.

We will look at rust_binary and rust_library next.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

174 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Rust Binaries
Let us start with a simple application. At the root of an AOSP checkout, create the following
files:

hello_rust/Android.bp:

rust_binary {
name: "hello_rust",
crate_name: "hello_rust",
srcs: ["src/main.rs"],
}

hello_rust/src/main.rs:

//! Rust demo.

/// Prints a greeting to standard output.


fn main() {
println!("Hello from Rust!");
}

You can now build, push, and run the binary:

m hello_rust
adb push "$ANDROID_PRODUCT_OUT/system/bin/hello_rust /data/local/tmp"
adb shell /data/local/tmp/hello_rust

Hello from Rust!

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

175 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Rust Libraries
You use rust_library to create a new Rust library for Android.

Here we declare a dependency on two libraries:

• libgreeting , which we define below,


• libtextwrap , which is a crate already vendored in external/rust/crates/ .

hello_rust/Android.bp:

rust_binary {
name: "hello_rust_with_dep",
crate_name: "hello_rust_with_dep",
srcs: ["src/main.rs"],
rustlibs: [
"libgreetings",
"libtextwrap",
],
prefer_rlib: true,
}

rust_library {
name: "libgreetings",
crate_name: "greetings",
srcs: ["src/lib.rs"],
}

hello_rust/src/main.rs:

//! Rust demo.

use greetings::greeting;
use textwrap::fill;

/// Prints a greeting to standard output.


fn main() {
println!("{}", fill(&greeting("Bob"), 24));
}

hello_rust/src/lib.rs:

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

176 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

//! Greeting library.

/// Greet `name`.


pub fn greeting(name: &str) -> String {
format!("Hello {name}, it is very nice to meet you!")
}

You build, push, and run the binary like before:

m hello_rust_with_dep
adb push "$ANDROID_PRODUCT_OUT/system/bin/hello_rust_with_dep /data/local/tmp"
adb shell /data/local/tmp/hello_rust_with_dep

Hello Bob, it is very


nice to meet you!

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

177 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

AIDL
The Android Interface Definition Language (AIDL) is supported in Rust:

• Rust code can call existing AIDL servers,


• You can create new AIDL servers in Rust.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

178 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

AIDL Interfaces
You declare the API of your service using an AIDL interface:

birthday_service/aidl/com/example/birthdayservice/IBirthdayService.aidl:

package com.example.birthdayservice;

/** Birthday service interface. */


interface IBirthdayService {
/** Generate a Happy Birthday message. */
String wishHappyBirthday(String name, int years);
}

birthday_service/aidl/Android.bp:

aidl_interface {
name: "com.example.birthdayservice",
srcs: ["com/example/birthdayservice/*.aidl"],
unstable: true,
backend: {
rust: { // Rust is not enabled by default
enabled: true,
},
},
}

Add vendor_available: true if your AIDL file is used by a binary in the vendor partition.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

179 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Service Implementation
We can now implement the AIDL service:

birthday_service/src/lib.rs:

//! Implementation of the `IBirthdayService` AIDL interface.


use
com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayServ
ice::IBirthdayService;
use com_example_birthdayservice::binder;

/// The `IBirthdayService` implementation.


pub struct BirthdayService;

impl binder::Interface for BirthdayService {}

impl IBirthdayService for BirthdayService {


fn wishHappyBirthday(&self, name: &str, years: i32) ->
binder::Result<String> {
Ok(format!(
"Happy Birthday {name}, congratulations with the {years} years!"
))
}
}

birthday_service/Android.bp:

rust_library {
name: "libbirthdayservice",
srcs: ["src/lib.rs"],
crate_name: "birthdayservice",
rustlibs: [
"com.example.birthdayservice-rust",
"libbinder_rs",
],
}

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

180 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

AIDL Server
Finally, we can create a server which exposes the service:

birthday_service/src/server.rs:

//! Birthday service.


use birthdayservice::BirthdayService;
use
com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayServ
ice::BnBirthdayService;
use com_example_birthdayservice::binder;

const SERVICE_IDENTIFIER: &str = "birthdayservice";

/// Entry point for birthday service.


fn main() {
let birthday_service = BirthdayService;
let birthday_service_binder = BnBirthdayService::new_binder(
birthday_service,
binder::BinderFeatures::default(),
);
binder::add_service(SERVICE_IDENTIFIER,
birthday_service_binder.as_binder())
.expect("Failed to register service");
binder::ProcessState::join_thread_pool()
}

birthday_service/Android.bp:

rust_binary {
name: "birthday_server",
crate_name: "birthday_server",
srcs: ["src/server.rs"],
rustlibs: [
"com.example.birthdayservice-rust",
"libbinder_rs",
"libbirthdayservice",
],
prefer_rlib: true,
}

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

181 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Deploy
We can now build, push, and start the service:

m birthday_server
adb push "$ANDROID_PRODUCT_OUT/system/bin/birthday_server /data/local/tmp"
adb shell /data/local/tmp/birthday_server

In another terminal, check that the service runs:

adb shell service check birthdayservice

Service birthdayservice: found

You can also call the service with service call :

adb shell service call birthdayservice 1 s16 Bob i32 24

Result: Parcel(
0x00000000: 00000000 00000036 00610048 00700070 '....6...H.a.p.p.'
0x00000010: 00200079 00690042 00740072 00640068 'y. .B.i.r.t.h.d.'
0x00000020: 00790061 00420020 0062006f 0020002c 'a.y. .B.o.b.,. .'
0x00000030: 006f0063 0067006e 00610072 00750074 'c.o.n.g.r.a.t.u.'
0x00000040: 0061006c 00690074 006e006f 00200073 'l.a.t.i.o.n.s. .'
0x00000050: 00690077 00680074 00740020 00650068 'w.i.t.h. .t.h.e.'
0x00000060: 00320020 00200034 00650079 00720061 ' .2.4. .y.e.a.r.'
0x00000070: 00210073 00000000 's.!..... ')

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

182 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

AIDL Client
Finally, we can create a Rust client for our new service.

birthday_service/src/client.rs:

//! Birthday service.


use
com_example_birthdayservice::aidl::com::example::birthdayservice::IBirthdayServ
ice::IBirthdayService;
use com_example_birthdayservice::binder;

const SERVICE_IDENTIFIER: &str = "birthdayservice";

/// Connect to the BirthdayService.


pub fn connect() -> Result<binder::Strong<dyn IBirthdayService>,
binder::StatusCode> {
binder::get_interface(SERVICE_IDENTIFIER)
}

/// Call the birthday service.


fn main() -> Result<(), binder::Status> {
let name = std::env::args()
.nth(1)
.unwrap_or_else(|| String::from("Bob"));
let years = std::env::args()
.nth(2)
.and_then(|arg| arg.parse::<i32>().ok())
.unwrap_or(42);

binder::ProcessState::start_thread_pool();
let service = connect().expect("Failed to connect to BirthdayService");
let msg = service.wishHappyBirthday(&name, years)?;
println!("{msg}");
Ok(())
}

birthday_service/Android.bp:

rust_binary {
name: "birthday_client",
crate_name: "birthday_client",
srcs: ["src/client.rs"],
rustlibs: [
"com.example.birthdayservice-rust",
"libbinder_rs",
],
This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
prefer_rlib: true,
}
traffic. Learn more OK, got it

183 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Notice that the client does not depend on libbirthdayservice .

Build, push, and run the client on your device:

m birthday_client
adb push "$ANDROID_PRODUCT_OUT/system/bin/birthday_client /data/local/tmp"
adb shell /data/local/tmp/birthday_client Charlie 60

Happy Birthday Charlie, congratulations with the 60 years!

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

184 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Changing API
Let us extend the API with more functionality: we want to let clients specify a list of lines for
the birthday card:

package com.example.birthdayservice;

/** Birthday service interface. */


interface IBirthdayService {
/** Generate a Happy Birthday message. */
String wishHappyBirthday(String name, int years, in String[] text);
}

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

185 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Logging
You should use the log crate to automatically log to logcat (on-device) or stdout (on-
host):

hello_rust_logs/Android.bp:

rust_binary {
name: "hello_rust_logs",
crate_name: "hello_rust_logs",
srcs: ["src/main.rs"],
rustlibs: [
"liblog_rust",
"liblogger",
],
prefer_rlib: true,
host_supported: true,
}

hello_rust_logs/src/main.rs:

//! Rust logging demo.

use log::{debug, error, info};

/// Logs a greeting.


fn main() {
logger::init(
logger::Config::default()
.with_tag_on_device("rust")
.with_min_level(log::Level::Trace),
);
debug!("Starting program.");
info!("Things are going fine.");
error!("Something went wrong!");
}

Build, push, and run the binary on your device:

m hello_rust_logs
adb push "$ANDROID_PRODUCT_OUT/system/bin/hello_rust_logs /data/local/tmp"
adb shell /data/local/tmp/hello_rust_logs

The logs show up in adb logcat :


This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
adb logcat
traffic. -s rust
Learn more OK, got it

186 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

09-08 08:38:32.454 2420 2420 D rust: hello_rust_logs: Starting program.


09-08 08:38:32.454 2420 2420 I rust: hello_rust_logs: Things are going fine.
09-08 08:38:32.454 2420 2420 E rust: hello_rust_logs: Something went wrong!

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

187 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Interoperability
Rust has excellent support for interoperability with other languages. This means that you
can:

• Call Rust functions from other languages.


• Call functions written in other languages from Rust.

When you call functions in a foreign language we say that you’re using a foreign function
interface, also known as FFI.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

188 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Interoperability with C
Rust has full support for linking object files with a C calling convention. Similarly, you can
export Rust functions and call them from C.

You can do it by hand if you want:

extern "C" {
fn abs(x: i32) -> i32;
}

fn main() {
let x = -42;
let abs_x = unsafe { abs(x) };
println!("{x}, {abs_x}");
}

We already saw this in the Safe FFI Wrapper exercise.

This assumes full knowledge of the target platform. Not recommended for production.

We will look at better options next.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

189 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Using Bindgen
The bindgen tool can auto-generate bindings from a C header file.

First create a small C library:

interoperability/bindgen/libbirthday.h:

typedef struct card {


const char* name;
int years;
} card;

void print_card(const card* card);

interoperability/bindgen/libbirthday.c:

#include <stdio.h>
#include "libbirthday.h"

void print_card(const card* card) {


printf("+--------------\n");
printf("| Happy Birthday %s!\n", card->name);
printf("| Congratulations with the %i years!\n", card->years);
printf("+--------------\n");
}

Add this to your Android.bp file:

interoperability/bindgen/Android.bp:

cc_library {
name: "libbirthday",
srcs: ["libbirthday.c"],
}

Create a wrapper header file for the library (not strictly needed in this example):

interoperability/bindgen/libbirthday_wrapper.h:

#include "libbirthday.h"

You can now auto-generate the bindings:


This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
interoperability/bindgen/Android.bp:
traffic. Learn more OK, got it

190 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

rust_bindgen {
name: "libbirthday_bindgen",
crate_name: "birthday_bindgen",
wrapper_src: "libbirthday_wrapper.h",
source_stem: "bindings",
static_libs: ["libbirthday"],
}

Finally, we can use the bindings in our Rust program:

interoperability/bindgen/Android.bp:

rust_binary {
name: "print_birthday_card",
srcs: ["main.rs"],
rustlibs: ["libbirthday_bindgen"],
}

interoperability/bindgen/main.rs:

//! Bindgen demo.

use birthday_bindgen::{card, print_card};

fn main() {
let name = std::ffi::CString::new("Peter").unwrap();
let card = card {
name: name.as_ptr(),
years: 42,
};
unsafe {
print_card(&card as *const card);
}
}

Build, push, and run the binary on your device:

m print_birthday_card
adb push "$ANDROID_PRODUCT_OUT/system/bin/print_birthday_card /data/local/tmp"
adb shell /data/local/tmp/print_birthday_card

Finally, we can run auto-generated tests to ensure the bindings work:

interoperability/bindgen/Android.bp:

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

191 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

rust_test {
name: "libbirthday_bindgen_test",
srcs: [":libbirthday_bindgen"],
crate_name: "libbirthday_bindgen_test",
test_suites: ["general-tests"],
auto_gen_config: true,
clippy_lints: "none", // Generated file, skip linting
lints: "none",
}

atest libbirthday_bindgen_test

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

192 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Calling Rust
Exporting Rust functions and types to C is easy:

interoperability/rust/libanalyze/analyze.rs

1 //! Rust FFI demo.


2 #![deny(improper_ctypes_definitions)]
3
4 use std::os::raw::c_int;
5
6 /// Analyze the numbers.
7 #[no_mangle]
8 pub extern "C" fn analyze_numbers(x: c_int, y: c_int) {
9 if x < y {
10 println!("x ({x}) is smallest!");
11 } else {
12 println!("y ({y}) is probably larger than x ({x})");
13 }
14 }

interoperability/rust/libanalyze/analyze.h

#ifndef ANALYSE_H
#define ANALYSE_H

extern "C" {
void analyze_numbers(int x, int y);
}

#endif

interoperability/rust/libanalyze/Android.bp

rust_ffi {
name: "libanalyze_ffi",
crate_name: "analyze_ffi",
srcs: ["analyze.rs"],
include_dirs: ["."],
}

We can now call this from a C binary:

interoperability/rust/analyze/main.c

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

193 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

#include "analyze.h"

int main() {
analyze_numbers(10, 20);
analyze_numbers(123, 123);
return 0;
}

interoperability/rust/analyze/Android.bp

cc_binary {
name: "analyze_numbers",
srcs: ["main.c"],
static_libs: ["libanalyze_ffi"],
}

Build, push, and run the binary on your device:

m analyze_numbers
adb push "$ANDROID_PRODUCT_OUT/system/bin/analyze_numbers /data/local/tmp"
adb shell /data/local/tmp/analyze_numbers

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

194 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

With C++
The CXX crate makes it possible to do safe interoperability between Rust and C++.

The overall approach looks like this:

See the CXX tutorial for an full example of using this.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

195 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Interoperability with Java


Java can load shared objects via Java Native Interface (JNI). The jni crate allows you to
create a compatible library.

First, we create a Rust function to export to Java:

interoperability/java/src/lib.rs:

//! Rust <-> Java FFI demo.

use jni::objects::{JClass, JString};


use jni::sys::jstring;
use jni::JNIEnv;

/// HelloWorld::hello method implementation.


#[no_mangle]
pub extern "system" fn Java_HelloWorld_hello(
env: JNIEnv,
_class: JClass,
name: JString,
) -> jstring {
let input: String = env.get_string(name).unwrap().into();
let greeting = format!("Hello, {input}!");
let output = env.new_string(greeting).unwrap();
output.into_inner()
}

interoperability/java/Android.bp:

rust_ffi_shared {
name: "libhello_jni",
crate_name: "hello_jni",
srcs: ["src/lib.rs"],
rustlibs: ["libjni"],
}

Finally, we can call this function from Java:

interoperability/java/HelloWorld.java:

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

196 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

class HelloWorld {
private static native String hello(String name);

static {
System.loadLibrary("hello_jni");
}

public static void main(String[] args) {


String output = HelloWorld.hello("Alice");
System.out.println(output);
}
}

interoperability/java/Android.bp:

java_binary {
name: "helloworld_jni",
srcs: ["HelloWorld.java"],
main_class: "HelloWorld",
required: ["libhello_jni"],
}

Finally, you can build, sync, and run the binary:

m helloworld_jni
adb sync # requires adb root && adb remount
adb shell /system/bin/helloworld_jni

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

197 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Exercises
This is a group exercise: We will look at one of the projects you work with and try to
integrate some Rust into it. Some suggestions:

• Call your AIDL service with a client written in Rust.

• Move a function from your project to Rust and call it.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

198 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Welcome to Bare Metal Rust


This is a standalone one-day course about bare-metal Rust, aimed at people who are
familiar with the basics of Rust (perhaps from completing the Comprehensive Rust course),
and ideally also have some experience with bare-metal programming in some other
language such as C.

Today we will talk about ‘bare-metal’ Rust: running Rust code without an OS underneath us.
This will be divided into several parts:

• What is no_std Rust?


• Writing firmware for microcontrollers.
• Writing bootloader / kernel code for application processors.
• Some useful crates for bare-metal Rust development.

For the microcontroller part of the course we will use the BBC micro:bit v2 as an example.
It’s a development board based on the Nordic nRF51822 microcontroller with some LEDs
and buttons, an I2C-connected accelerometer and compass, and an on-board SWD
debugger.

To get started, install some tools we’ll need later. On gLinux or Debian:

sudo apt install gcc-aarch64-linux-gnu gdb-multiarch libudev-dev picocom pkg-


config qemu-system-arm
rustup update
rustup target add aarch64-unknown-none thumbv7em-none-eabihf
rustup component add llvm-tools-preview
cargo install cargo-binutils cargo-embed

And give users in the plugdev group access to the micro:bit programmer:

echo 'SUBSYSTEM=="usb", ATTR{idVendor}=="0d28", MODE="0664", GROUP="plugdev"'


|\
sudo tee /etc/udev/rules.d/50-microbit.rules
sudo udevadm control --reload-rules

On MacOS:

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

199 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

xcode-select --install
brew install gdb picocom qemu
brew install --cask gcc-aarch64-embedded
rustup update
rustup target add aarch64-unknown-none thumbv7em-none-eabihf
rustup component add llvm-tools-preview
cargo install cargo-binutils cargo-embed

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

200 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

no_std

core alloc std

• Slices, &str , CStr • Box , Cow , Arc , • Error


• NonZeroU8 … Rc • HashMap
• Option , Result • Vec , BinaryHeap , • Mutex , Condvar ,
• Display , Debug , BtreeMap , Barrier , Once ,
write! … LinkedList , RwLock , mpsc
• Iterator VecDeque • File and the rest
• panic! , • String , CString , of fs
assert_eq! … format! • println! , Read ,
• NonNull and all Write , Stdin ,
the usual pointer- Stdout and the rest
related functions of io
• Future and • Path , OsString
async / await • net
• fence , • Command , Child ,
AtomicBool , ExitCode
AtomicPtr , • spawn , sleep and
AtomicU32 … the rest of thread
• Duration • SystemTime ,
Instant

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

201 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

A minimal no_std program


1 #![no_main]
2 #![no_std]
3
4 use core::panic::PanicInfo;
5
6 #[panic_handler]
7 fn panic(_panic: &PanicInfo) -> ! {
8 loop {}
9 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

202 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

alloc
To use alloc you must implement a global (heap) allocator.

1 #![no_main]
2 #![no_std]
3
4 extern crate alloc;
5 extern crate panic_halt as _;
6
7 use alloc::string::ToString;
8 use alloc::vec::Vec;
9 use buddy_system_allocator::LockedHeap;
10
11 #[global_allocator]
12 static HEAP_ALLOCATOR: LockedHeap<32> = LockedHeap::<32>::new();
13
14 static mut HEAP: [u8; 65536] = [0; 65536];
15
16 pub fn entry() {
17 // Safe because `HEAP` is only used here and `entry` is only called once.
18 unsafe {
19 // Give the allocator some memory to allocate.
20 HEAP_ALLOCATOR
21 .lock()
22 .init(HEAP.as_mut_ptr() as usize, HEAP.len());
23 }
24
25 // Now we can do things that require heap allocation.
26 let mut v = Vec::new();
27 v.push("A string".to_string());
28 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

203 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Microcontrollers
The cortex_m_rt crate provides (among other things) a reset handler for Cortex M
microcontrollers.

1 #![no_main]
2 #![no_std]
3
4 extern crate panic_halt as _;
5
6 mod interrupts;
7
8 use cortex_m_rt::entry;
9
10 #[entry]
11 fn main() -> ! {
12 loop {}
13 }

Next we’ll look at how to access peripherals, with increasing levels of abstraction.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

204 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Raw MMIO
Most microcontrollers access peripherals via memory-mapped IO. Let’s try turning on an
LED on our micro:bit:

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

205 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

1 #![no_main]
2 #![no_std]
3
4 extern crate panic_halt as _;
5
6 mod interrupts;
7
8 use core::mem::size_of;
9 use cortex_m_rt::entry;
10
11 /// GPIO port 0 peripheral address
12 const GPIO_P0: usize = 0x5000_0000;
13
14 // GPIO peripheral offsets
15 const PIN_CNF: usize = 0x700;
16 const OUTSET: usize = 0x508;
17 const OUTCLR: usize = 0x50c;
18
19 // PIN_CNF fields
20 const DIR_OUTPUT: u32 = 0x1;
21 const INPUT_DISCONNECT: u32 = 0x1 << 1;
22 const PULL_DISABLED: u32 = 0x0 << 2;
23 const DRIVE_S0S1: u32 = 0x0 << 8;
24 const SENSE_DISABLED: u32 = 0x0 << 16;
25
26 #[entry]
27 fn main() -> ! {
28 // Configure GPIO 0 pins 21 and 28 as push-pull outputs.
29 let pin_cnf_21 = (GPIO_P0 + PIN_CNF + 21 * size_of::<u32>()) as *mut u32
30 let pin_cnf_28 = (GPIO_P0 + PIN_CNF + 28 * size_of::<u32>()) as *mut u32
31 // Safe because the pointers are to valid peripheral control registers, and
32 // no aliases exist.
33 unsafe {
34 pin_cnf_21.write_volatile(
35 DIR_OUTPUT | INPUT_DISCONNECT | PULL_DISABLED | DRIVE_S0S1 | SENSE_DISAB
36 );
37 pin_cnf_28.write_volatile(
38 DIR_OUTPUT | INPUT_DISCONNECT | PULL_DISABLED | DRIVE_S0S1 | SENSE_DISAB
39 );
40 }
41
42 // Set pin 28 low and pin 21 high to turn the LED on.
43 let gpio0_outset = (GPIO_P0 + OUTSET) as *mut u32;
44 let gpio0_outclr = (GPIO_P0 + OUTCLR) as *mut u32;
45 // Safe because the pointers are to valid peripheral control registers, and
46 // no aliases exist.
47 unsafe {
48 gpio0_outclr.write_volatile(1 << 28);
49 gpio0_outset.write_volatile(1 << 21);
50 }
This51site uses cookies from Google to deliver and enhance the quality of its services and to analyze
52 loop {}
traffic. Learn more OK, got it
53 }

206 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

207 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Peripheral Access Crates


svd2rust generates mostly-safe Rust wrappers for memory-mapped peripherals from
CMSIS-SVD files.

1 #![no_main]
2 #![no_std]
3
4 extern crate panic_halt as _;
5
6 use cortex_m_rt::entry;
7 use nrf52833_pac::Peripherals;
8
9 #[entry]
10 fn main() -> ! {
11 let p = Peripherals::take().unwrap();
12 let gpio0 = p.P0;
13
14 // Configure GPIO 0 pins 21 and 28 as push-pull outputs.
15 gpio0.pin_cnf[21].write(|w| {
16 w.dir().output();
17 w.input().disconnect();
18 w.pull().disabled();
19 w.drive().s0s1();
20 w.sense().disabled();
21 w
22 });
23 gpio0.pin_cnf[28].write(|w| {
24 w.dir().output();
25 w.input().disconnect();
26 w.pull().disabled();
27 w.drive().s0s1();
28 w.sense().disabled();
29 w
30 });
31
32 // Set pin 28 low and pin 21 high to turn the LED on.
33 gpio0.outclr.write(|w| w.pin28().clear());
34 gpio0.outset.write(|w| w.pin21().set());
35
36 loop {}
37 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

208 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

HAL crates
HAL crates for many microcontrollers provide wrappers around various peripherals. These
generally implement traits from embedded-hal .

1 #![no_main]
2 #![no_std]
3
4 extern crate panic_halt as _;
5
6 use cortex_m_rt::entry;
7 use nrf52833_hal::gpio::{p0, Level};
8 use nrf52833_hal::pac::Peripherals;
9 use nrf52833_hal::prelude::*;
10
11 #[entry]
12 fn main() -> ! {
13 let p = Peripherals::take().unwrap();
14
15 // Create HAL wrapper for GPIO port 0.
16 let gpio0 = p0::Parts::new(p.P0);
17
18 // Configure GPIO 0 pins 21 and 28 as push-pull outputs.
19 let mut col1 = gpio0.p0_28.into_push_pull_output(Level::High);
20 let mut row1 = gpio0.p0_21.into_push_pull_output(Level::Low);
21
22 // Set pin 28 low and pin 21 high to turn the LED on.
23 col1.set_low().unwrap();
24 row1.set_high().unwrap();
25
26 loop {}
27 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

209 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Board support crates


Board support crates provide a further level of wrapping for a specific board for
convenience.

1 #![no_main]
2 #![no_std]
3
4 extern crate panic_halt as _;
5
6 use cortex_m_rt::entry;
7 use microbit::hal::prelude::*;
8 use microbit::Board;
9
10 #[entry]
11 fn main() -> ! {
12 let mut board = Board::take().unwrap();
13
14 board.display_pins.col1.set_low().unwrap();
15 board.display_pins.row1.set_high().unwrap();
16
17 loop {}
18 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

210 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

The type state pattern


1 #[entry]
2 fn main() -> ! {
3 let p = Peripherals::take().unwrap();
4 let gpio0 = p0::Parts::new(p.P0);
5
6 let pin: P0_01<Disconnected> = gpio0.p0_01;
7
8 // let gpio0_01_again = gpio0.p0_01; // Error, moved.
9 let pin_input: P0_01<Input<Floating>> = pin.into_floating_input();
10 if pin_input.is_high().unwrap() {
11 // ...
12 }
13 let mut pin_output: P0_01<Output<OpenDrain>> = pin_input
14 .into_open_drain_output(OpenDrainConfig::Disconnect0Standard1, Level::
15 pin_output.set_high().unwrap();
16 // pin_input.is_high(); // Error, moved.
17
18 let _pin2: P0_02<Output<OpenDrain>> = gpio0
19 .p0_02
20 .into_open_drain_output(OpenDrainConfig::Disconnect0Standard1, Level::
21 let _pin3: P0_03<Output<PushPull>> = gpio0.p0_03.into_push_pull_output(Level::
22
23 loop {}
24 }

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

211 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

embedded-hal
The embedded-hal crate provides a number of traits covering common microcontroller
peripherals.

• GPIO
• ADC
• I2C, SPI, UART, CAN
• RNG
• Timers
• Watchdogs

Other crates then implement drivers in terms of these traits, e.g. an accelerometer driver
might need an I2C or SPI bus implementation.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

212 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

probe-rs, cargo-embed
probe-rs is a handy toolset for embedded debugging, like OpenOCD but better integrated.

• SWD and JTAG via CMSIS-DAP, ST-Link and J-Link probes


• GDB stub and Microsoft DAP server
• Cargo integration

cargo-embed is a cargo subcommand to build and flash binaries, log RTT output and
connect GDB. It’s configured by an Embed.toml file in your project directory.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

213 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Debugging
Embed.toml:

[default.general]
chip = "nrf52833_xxAA"

[debug.gdb]
enabled = true

In one terminal under src/bare-metal/microcontrollers/examples/ :

cargo embed --bin board_support debug

In another terminal in the same directory:

gdb-multiarch target/thumbv7em-none-eabihf/debug/board_support --eval-


command="target remote :1337"

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

214 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Other projects
• RTIC
◦ “Real-Time Interrupt-driven Concurrency”
◦ Shared resource management, message passing, task scheduling, timer queue
• Embassy
◦ async executors with priorities, timers, networking, USB
• TockOS
◦ Security-focused RTOS with preemptive scheduling and Memory Protection Unit
support
• Hubris
◦ Microkernel RTOS from Oxide Computer Company with memory protection,
unprivileged drivers, IPC
• Bindings for FreeRTOS
• Some platforms have std implementations, e.g. esp-idf.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

215 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Exercises
We will read the direction from an I2C compass, and log the readings to a serial port.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

216 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Compass
We will read the direction from an I2C compass, and log the readings to a serial port. If you
have time, try displaying it on the LEDs somehow too, or use the buttons somehow.

Hints:

• Check the documentation for the lsm303agr and microbit-v2 crates, as well as the
micro:bit hardware.
• The LSM303AGR Inertial Measurement Unit is connected to the internal I2C bus.
• TWI is another name for I2C, so the I2C master peripheral is called TWIM.
• The LSM303AGR driver needs something implementing the
embedded_hal::blocking::i2c::WriteRead trait. The microbit::hal::Twim struct
implements this.
• You have a microbit::Board struct with fields for the various pins and peripherals.
• You can also look at the nRF52833 datasheet if you want, but it shouldn’t be necessary
for this exercise.

Download the exercise template and look in the compass directory for the following files.

src/main.rs :

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

217 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

#![no_main]
#![no_std]

extern crate panic_halt as _;

use core::fmt::Write;
use cortex_m_rt::entry;
use microbit::{hal::uarte::{Baudrate, Parity, Uarte}, Board};

#[entry]
fn main() -> ! {
let board = Board::take().unwrap();

// Configure serial port.


let mut serial = Uarte::new(
board.UARTE0,
board.uart.into(),
Parity::EXCLUDED,
Baudrate::BAUD115200,
);

// Set up the I2C controller and Inertial Measurement Unit.


// TODO

writeln!(serial, "Ready.").unwrap();

loop {
// Read compass data and log it to the serial port.
// TODO
}
}

Cargo.toml (you shouldn’t need to change this):

[workspace]

[package]
name = "compass"
version = "0.1.0"
edition = "2021"
publish = false

[dependencies]
cortex-m-rt = "0.7.3"
embedded-hal = "0.2.6"
lsm303agr = "0.2.2"
microbit-v2 = "0.13.0"
panic-halt = "0.2.0"
This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
Learn (you
Embed.toml
traffic. more shouldn’t
OK, got itneed to change this):

218 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

[default.general]
chip = "nrf52833_xxAA"

[debug.gdb]
enabled = true

[debug.reset]
halt_afterwards = true

.cargo/config.toml (you shouldn’t need to change this):

[build]
target = "thumbv7em-none-eabihf" # Cortex-M4F

[target.'cfg(all(target_arch = "arm", target_os = "none"))']


rustflags = ["-C", "link-arg=-Tlink.x"]

See the serial output on Linux with:

picocom --baud 115200 --imap lfcrlf /dev/ttyACM0

Or on Mac OS something like (the device name may be slightly different):

picocom --baud 115200 --imap lfcrlf /dev/tty.usbmodem14502

Use Ctrl+A Ctrl+Q to quit picocom.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

219 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Application processors
So far we’ve talked about microcontrollers, such as the Arm Cortex-M series. Now let’s try
writing something for Cortex-A. For simplicity we’ll just work with QEMU’s aarch64 ‘virt’
board.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

220 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Getting Ready to Rust


Before we can start running Rust code, we need to do some initialisation.

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

221 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

.section .init.entry, "ax"


.global entry
entry:
/*
* Load and apply the memory management configuration, ready to enable MMU
and
* caches.
*/
adrp x30, idmap
msr ttbr0_el1, x30

mov_i x30, .Lmairval


msr mair_el1, x30

mov_i x30, .Ltcrval


/* Copy the supported PA range into TCR_EL1.IPS. */
mrs x29, id_aa64mmfr0_el1
bfi x30, x29, #32, #4

msr tcr_el1, x30

mov_i x30, .Lsctlrval

/*
* Ensure everything before this point has completed, then invalidate any
* potentially stale local TLB entries before they start being used.
*/
isb
tlbi vmalle1
ic iallu
dsb nsh
isb

/*
* Configure sctlr_el1 to enable MMU and cache and don't proceed until this
* has completed.
*/
msr sctlr_el1, x30
isb

/* Disable trapping floating point access in EL1. */


mrs x30, cpacr_el1
orr x30, x30, #(0x3 << 20)
msr cpacr_el1, x30
isb

/* Zero out the bss section. */


adr_l x29, bss_begin
adr_l x30, bss_end
0: cmp x29, x30
This site uses1f
b.hs cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. stp xzr,
Learn xzr,OK,
more [x29],
got it #16

222 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

b 0b

1: /* Prepare the stack. */


adr_l x30, boot_stack_end
mov sp, x30

/* Set up exception vector. */


adr x30, vector_table_el1
msr vbar_el1, x30

/* Call into Rust code. */


bl main

/* Loop forever waiting for interrupts. */


2: wfi
b 2b

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

223 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Inline assembly
Sometimes we need to use assembly to do things that aren’t possible with Rust code. For
example, to make an HVC to tell the firmware to power off the system:

1 #![no_main]
2 #![no_std]
3
4 use core::arch::asm;
5 use core::panic::PanicInfo;
6
7 mod exceptions;
8
9 const PSCI_SYSTEM_OFF: u32 = 0x84000008;
10
11 #[no_mangle]
12 extern "C" fn main(_x0: u64, _x1: u64, _x2: u64, _x3: u64) {
13 // Safe because this only uses the declared registers and doesn't do
14 // anything with memory.
15 unsafe {
16 asm!("hvc #0",
17 inout("w0") PSCI_SYSTEM_OFF => _,
18 inout("w1") 0 => _,
19 inout("w2") 0 => _,
20 inout("w3") 0 => _,
21 inout("w4") 0 => _,
22 inout("w5") 0 => _,
23 inout("w6") 0 => _,
24 inout("w7") 0 => _,
25 options(nomem, nostack)
26 );
27 }
28
29 loop {}
30 }

(If you actually want to do this, use the smccc crate which has wrappers for all these
functions.)

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

224 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Volatile memory access for MMIO


• Use pointer::read_volatile and pointer::write_volatile .
• Never hold a reference.
• addr_of! lets you get fields of structs without creating an intermediate reference.

▸ Details

This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it

225 of 226 7/21/2023, 10:40 PM


Comprehensive R... https://google.github.io/comprehensive-rust/print.html

Let’s write a UART driver


The QEMU ‘virt’ machine has a PL011 UART, so let’s write a driver for that.

1 const FLAG_REGISTER_OFFSET: usize = 0x18;


2 const FR_BUSY: u8 = 1 << 3;
3 const FR_TXFF: u8 = 1 << 5;
4
5 /// Minimal driver for a PL011 UART.
6 #[derive(Debug)]
7 pub struct Uart {
8 base_address: *mut u8,
9 }
10
11 impl Uart {
12 /// Constructs a new instance of the UART driver for a PL011 device at the
13 /// given base address.
14 ///
15 /// # Safety
16 ///
17 /// The given base address must point to the 8 MMIO control registers of a
18 /// PL011 device, which must be mapped into the address space of the process
19 /// as device memory and not have any other aliases.
20 pub unsafe fn new(base_address: *mut u8) -> Self {
21 Self { base_address }
22 }
23
24 /// Writes a single byte to the UART.
25 pub fn write_byte(&self, byte: u8) {
26 // Wait until there is room in the TX buffer.
27 while self.read_flag_register() & FR_TXFF != 0 {}
28
29 // Safe because we know that the base address points to the control
30 // registers of a PL011 device which is appropriately mapped.
31 unsafe {
32 // Write to the TX buffer.
33 self.base_address.write_volatile(byte);
34 }
35
36 // Wait until the UART is no longer busy.
37 while self.read_flag_register() & FR_BUSY != 0 {}
38 }
39
40 fn read_flag_register(&self) -> u8 {
41 // Safe because we know that the base address points to the control
42 // registers of a PL011 device which is appropriately mapped.
43 unsafe { self.base_address.add(FLAG_REGISTER_OFFSET).read_volatile()
44 }
45 }
This site uses cookies from Google to deliver and enhance the quality of its services and to analyze
traffic. Learn more OK, got it
▸ Details

226 of 226 7/21/2023, 10:40 PM

You might also like