[go: up one dir, main page]

0% found this document useful (0 votes)
168 views174 pages

Dhghomon: Easy - Rust

This document summarizes a GitHub repository that explains Rust programming concepts using simple English. The repository contains a textbook for learning Rust that covers topics like primitive types, variables, functions, control flow, enums, references, structs, generics, traits and more. It also describes the Rust Playground for running Rust code online without installing Rust and provides tips for using the Playground.

Uploaded by

Shade
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)
168 views174 pages

Dhghomon: Easy - Rust

This document summarizes a GitHub repository that explains Rust programming concepts using simple English. The repository contains a textbook for learning Rust that covers topics like primitive types, variables, functions, control flow, enums, references, structs, generics, traits and more. It also describes the Rust Playground for running Rust code online without installing Rust and provides tips for using the Playground.

Uploaded by

Shade
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/ 174

Dhghomon/easy_rust: Rust explained using easy English https://github.

com/Dhghomon/easy_rust

Dhghomon / easy_rust

Code Issues 4 Pull requests 3 Actions Projects Wiki

master Go to file Add file Code About

Rust explained
Dhghomon Pointer address … 10 hours ago 111 using easy English

Readme
LICENSE Initial commit 13 days ago

MIT License
README.… Pointer address 10 hours ago

README.md Releases

No releases published

Introduction
Contributors 6
Rust is a new language that already has good
textbooks. But sometimes its textbooks are difficult
because they are for native English speakers. Many
companies and people now learn Rust, and could learn
faster with a book that has easy English. This textbook
is for these companies and people to learn Rust with
simple English.

Rust Playground
Types
Primitive types
Chars
Type inference
Floats
Printing hello, world!
Declaring variables and code blocks

1 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Display and debug


Smallest and largest numbers
Mutability (changing)
Shadowing
The stack, the heap, and pointers
More about printing
Strings
const and static
More on references
Mutable references
Shadowing again
Giving references to functions
Copy types
Variables without values
Collection types
Arrays
Vectors
Tuples
Control flow
Structs
Enums
Enums to use multiple types
References and the dot operator
Destructuring
Loops
Implementing structs and enums
Self
Generics
Option and Result
Result
The ? operator
When panic and unwrap are good
Traits

2 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Taking a String and a &str in a function


Chaining methods
Iterators
How does an iterator work?
Closures
|_| in a closure
The dbg! macro and .inspect
Types of &str
Lifetimes
Interior mutability
Cell
RefCell
Mutex
RwLock
Cow
Type aliases
Importing inside a function
The todo! macro
Rc
Multiple threads
Arc
Channels
Reading Rust documentation
assert_eq!
Searching
[src] button

Rust Playground

3 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Maybe you don't want to install Rust yet, and that's


okay. You can go to https://play.rust-lang.org/ and start
writing Rust. You can write your code there and click
Run to see the results.

Here are some tips when using the Rust Playground:

Run your code with Run


Change Debug to Release if you want your code to
be faster. Debug: compiles faster, runs slower,
contains debug information. Release: compiles
slower, runs much faster, removes debug
information.
Click on Share to get a url. You can use that to
share your code if you want help.
Tools: Rustfmt will format your code nicely.
Tools: Clippy will give you extra information about
how to make your code better.
Config: here you can change your theme to dark
mode, and many other configurations.

If you want to install Rust, go here https://www.rust-


lang.org/tools/install and follow the instructions. Usually
you will use rustup to install and update Rust.

Types

Primitive types

Rust has simple types that are called primitive types.


We will start with integers. Integers are whole numbers
with no decimal point. There are two types of integers:

Signed integers,
Unsigned integers.

4 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Signs means + (plus sign) and - (minus sign), so


signed integers can be positive or negative (e.g. +8, -8).
But unsigned integers can only be positive, because
they do not have a sign.

The signed integers are: i8 , i16 , i32 , i64 , i128 ,


and isize . The unsigned integers are: u8 , u16 ,
u32 , u64 , u128 , and usize .

The number after the i or the u means the number of


bits for the number, so numbers with more bits can be
larger. 8 bits = one byte, so i8 is one byte, i64 is 8
bytes, and so on. Number types with larger sizes can
hold larger numbers. For example, a u8 can hold up to
255, but a u16 can hold up to 65535. And a u128 can
hold up to
340282366920938463463374607431768211455.

So what is isize and usize ? This means the


number of bits on your type of computer. (This is called
the architecture of your computer.) So isize and
usize on a 32-bit computer is like i32 and u32 , and
isize and usize on a 64-bit computer is like i64
and u64 .

There are many reasons for the different types of


integers. One reason is computer performance: a
smaller number of bytes is faster to process. But here
are some other uses:

5 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Characters in Rust are called char . Every char has a


number: the letter A is number 65, while the character
友 ("friend" in Chinese) is number 21451. The list of
numbers is called "Unicode". Unicode uses smaller
numbers for characters that are used more, like A
through Z, or digits 0 through 9, or space. The
characters that are used most get numbers that are less
than 256, and they can fit into a u8 . This means that
Rust can safely cast a u8 into a char , using as .
(Cast u8 as char means "pretend u8 is a char ")

Casting with as is useful because Rust always needs


to know the type of the integer. For example, this will not
compile:

fn main() {
let my_number = 100; // We didn't write a type of integer,
// so Rust chooses i32. Rust always
// chooses i32 for integers if you don'
// tell it to use a different type
println!("{}", my_number as char);

Here is the reason:

error[E0604]: only `u8` can be cast as


`char`, not `i32`
--> src\main.rs:3:20
|
3 | println!("{}", my_number as char);
| ^^^^^^^^^^^^^^^^^

One easy way to fix this is with as . First we use as to


make my_number a u8 , then one more as to make it
a char . Now it will compile:

fn main() {

6 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

let my_number = 100;


println!("{}", my_number as u8 as char);
}

Chars
A char is one character. For a char , use '' instead
of "" .

All chars are 4 bytes. They are 4 bytes because some


characters in a string are more than one byte. For
example:

fn main() {
let slice = "Hello!";

let slice2 = " 안녕


println!("Slice is {:?} bytes.", std::mem::
!"; // Korean for "hi"
println!("Slice2 is {:?} bytes.", std::mem::
}

slice is six characters in length and six bytes, but


slice2 is three characters in length and seven bytes.
char needs to fit any character in any language, so it
is 4 bytes long.

Type inference

7 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Type inference means that if you don't tell the compiler


the type, but it can decide by itself, it will decide. The
compiler always needs to know the type of the
variables, but you don’t always need to tell it. For
example, for let my_number = 8 , my_number will be
an i32 . That is because the compiler chooses i32 for
integers if you don't tell it. But if you say let
my_number: u8 = 8 , it will make my_number a u8 ,
because you told it u8 .

So usually the compiler can guess. But sometimes you


need to tell it, for two reasons:

1. You are doing something very complex and the


compiler doesn't know the type you want.
2. You want a different type (for example, you want an
i128 , not an i32 ).

To specify a type, add a colon after the variable name.

fn main() {
let small_number: u8 = 10;
}

For numbers, you can say the type after the number.
You don't need a space - just type it right after the
number.

fn main() {
let small_number = 10u8; // 10u8 = 10 of type u8
}

You can also add _ if you want to make the number


easy to read.

fn main() {

8 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

let small_number = 10_u8; // This is easier to read


let big_number = 100_000_000_i32; // 100 million is easy to
}

The _ does not change the number. It is only to make


it easy for you to read. And it doesn't matter how many
_ you use:

fn main() {
let number = 0________u8;
let number2 = 1___6______2____4______i32;
println!("{}, {}", number, number2);
}

This prints 0, 1624 .

Floats

Floats are numbers with decimal points. 5.5 is a float,


and 6 is an integer. 5.0 is also a float, and even 5. is a
float.

fn main() {
let my_float = 5.; // Rust sees . and knows that it is a float
}

But the types are not called float , they are called f32
and f64 . It is the same as integers: the number after
f shows the number of bits. If you don't write the type,
Rust will choose f64 .

Of course, only floats of the same type can be used


together. So you can't add an f32 to an f64 .

fn main() {
let my_float: f64 = 5.0; // This is an f64

9 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

let my_other_float: f32 = 8.5; // This is an f32

let third_float = my_float + my_other_float;


}

When you try to run this, Rust will say:

error[E0308]: mismatched types


--> src\main.rs:5:34
|
5 | let third_float = my_float +
my_other_float;
|
^^^^^^^^^^^^^^ expected `f64`, found `f32`

The compiler writes "expected (type), found (type)"


when you use the wrong type. It reads your code like
this:

let my_float: f64 = 5.0; // The compiler sees an f64


let my_other_float: f32 = 8.5; // The compiler sees an f32. It is
let third_float = my_float + // The compiler sees a new variable.
let third_float = my_float + my_other_float; // But it found an f32

So when you see "expected (type), found (type)", you


must find why the compiler expected a different type.

Of course, with simple numbers it is easy to fix. You can


cast the f32 to an f64 with as :

fn main() {
let my_float: f64 = 5.0;
let my_other_float: f32 = 8.5;

let third_float = my_float + my_other_float as


}

10 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Or even more simply, remove the type declarations.


Rust will choose types that can add together.

fn main() {
let my_float = 5.0; // Rust will choose f64
let my_other_float = 8.5; // Here again it will choose f64

let third_float = my_float + my_other_float;


}

The Rust compiler is smart and will not choose f64 if


you need f32:

fn main() {
let my_float: f32 = 5.0;
let my_other_float = 8.5; // Rust will choose f32,

let third_float = my_float + my_other_float; // because it know


}

Printing hello, world!


A new Rust program always starts with this:

fn main() {
println!("Hello, world!");
}

fn means function,

main is the function that starts the program,

() means that we didn't give the function anything


to start.

{} is a code block.

11 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

println! is a macro that prints to the console. A


macro is like a function that writes code for you. Macros
have a ! after them. We will learn about making
macros later. For now, remember that ! means that it
is a macro.

To learn about the ; , we will create another function.


First, in main we will print a number 8:

fn main() {
println!("Hello, world number {}!", 8);
}

The {} in println! means "put the variable inside


here". This prints Hello world number 8 .

We can put more in:

fn main() {
println!("Hello, worlds number {} and {}!",
}

This prints Hello, worlds number 8 and 9! .

Now let's create the function.

fn main() {
println!("Hello, world number {}", number());
}

fn number() -> i32 {


8
}

This also prints Hello, world number 8! . When Rust


looks at number() it sees a function. This function:

Does not take anything (because it has () )

12 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Returns an i32 . The -> (called a "skinny arrow")


shows what the function returns.

Inside the function is just 8 . Because there is no ; ,


this is the value it returns. If it had a ; , it would not
return anything. Rust will not compile this if it has a ; ,
because the return is i32 and ; returns () , not
i32 :

fn main() {
println!("Hello, world number {}", number());
}

fn number() -> i32 {


8;
}

5 | fn number() -> i32 {


| ------ ^^^ expected `i32`, found
`()`
| |
| implicitly returns `()` as its body
has no tail or `return` expression
6 | 8;
| - help: consider removing this
semicolon

This means "you told me that number() returns an


i32 , but you added a ; so it doesn't return anything".
So the compiler suggests removing the semicolon.

You can also write return 8; but in Rust it is normal


to just remove the ; to return .

When you want to give variables to a function, put them


inside the () . You have to give them a name and write
the type.

13 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn main() {
multiply(8, 9); // We can give the numbers directly
let some_number = 10; // Or we can declare two variables
let some_other_number = 2;
multiply(some_number, some_other_number); // and put them in
}

fn multiply(number_one: i32, number_two: i32) {


let result = number_one * number_two;
println!("{} times {} is {}", number_one, number_two, result
}

Of course, we can also return an i32 :

fn main() {
let multiply_result = multiply(8, 9); // We used multiply()
}

fn multiply(number_one: i32, number_two: i32) ->


let result = number_one * number_two;
println!("{} times {} is {}", number_one, number_two, result
result // this is the i32 that we return
}

Declaring variables and code


blocks

Use let to declare a variable (declare a variable = tell


Rust to make a variable).

fn main() {
let my_number = 8;
println!("Hello, number {}", my_number);
}

14 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Variables start and end inside a code block {} . In this


example, my_number ends before we call println! ,
because it is inside its own code block.

fn main() {
{
let my_number = 8; // my_number starts here
// my_number ends here!
}

println!("Hello, number {}", my_number); // there is no my_n


// println!() can't
}

You can use a code block to return a value:

fn main() {
let my_number = {
let second_number = 8;
second_number + 9 // No semicolon, so the code block ret
// It works just like a function
};

println!("My number is: {}", my_number);


}

If you add a semicolon inside the block, it will return ()


(nothing):

fn main() {
let my_number = {
let second_number = 8; // declare second_number,
second_number + 9; // add 9 to second_number
// but we didn't return it!
// second_number dies now
};

println!("My number is: {:?}", my_number);


}

15 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

So why did we write {:?} and not {} ? We will talk


about that now.

Display and debug


Simple variables in Rust can be printed with {} inside
println!() . But some variables can't, and you need
to debug print. Debug print is printing for the
programmer, because it usually shows more
information. Debug sometimes doesn't look pretty,
because it has extra information to help you.

How do you know if you need {:?} and not {} ? The


compiler will tell you. For example:

fn main() {
let doesnt_print = ();
println!("This will not print: {}", doesnt_print);
}

When we run this, the compiler says:

error[E0277]: `()` doesn't implement


`std::fmt::Display`
--> src\main.rs:3:41
|
3 | println!("This will not print: {}",
doesnt_print);
|
^^^^^^^^^^^^ `()` cannot be formatted with
the default formatter
|
= help: the trait `std::fmt::Display` is
not implemented for `()`
= note: in format strings you may be able
to use `{:?}` (or {:#?} for pretty-print)
instead
= note: required by

16 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

`std::fmt::Display::fmt`
= note: this error originates in a macro
(in Nightly builds, run with -Z macro-
backtrace for more info)

This is a lot of information. But the important part is:


you may be able to use {:?} (or {:#?} for
pretty-print) instead . This means that you can try
{:?} , and also {:#?} ( {:#?} prints with different
formatting).

So Display means printing with {} , and Debug means


printing with {:?} .

One more thing: you can also use print!() without ln if


you don't want a new line.

fn main() {
print!("This will not print a new line");
println!(" so this will be on the same line"
}

This prints This will not print a new line so


this will be on the same line .

Smallest and largest numbers

If you want to see the smallest and biggest numbers,


you can use MIN and MAX.

fn main() {
println!("The smallest i8 is {} and the biggest i8 is {}."
println!("The smallest u8 is {} and the biggest u8 is {}."
println!("The smallest i16 is {} and the biggest i16 is {}."
println!("The smallest u16 is {} and the biggest u16 is {}."
println!("The smallest i32 is {} and the biggest i32 is {}."
println!("The smallest u32 is {} and the biggest u32 is {}."

17 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

println!("The smallest i64 is {} and the biggest i64 is {}."


println!("The smallest u64 is {} and the biggest u64 is {}."
println!("The smallest i128 is {} and the biggest i128 is {}
println!("The smallest u128 is {} and the biggest u128 is {}

This will print:

The smallest i8 is -128 and the biggest i8 is 127


The smallest u8 is 0 and the biggest u8 is 255.
The smallest i16 is -32768 and the biggest i16 is
The smallest u16 is 0 and the biggest u16 is 65535
The smallest i32 is -2147483648 and the biggest
The smallest u32 is 0 and the biggest u32 is 4294967295
The smallest i64 is -9223372036854775808 and the biggest
The smallest u64 is 0 and the biggest u64 is 1844674407370955161
The smallest i128 is -170141183460469231731687303715884105728
The smallest u128 is 0 and the biggest u128 is

Mutability (changing)
When you declare a variable with let , it is immutable
(cannot be changed).

This will not work:

fn main() {
let my_number = 8;
my_number = 10;
}

The compiler says: error[E0384]: cannot assign


twice to immutable variable my_number . This is
because variables are immutable if you only write let .

To change a variable, add mut :

18 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn main() {
let mut my_number = 8;
my_number = 10;
}

Now there is no problem.

However, you cannot change the type even with mut .


This will not work:

fn main() {
let mut my_variable = 8;
my_variable = "Hello, world!";
}

You will see the same "expected" message from the


compiler: expected integer, found &str . &str is a
string type that we will learn soon.

Shadowing

Shadowing means using let to declare a new variable


with the same name as another variable. It looks like
mutability, but it is completely different. Shadowing
looks like this:

fn main() {
let my_number = 8; // This is an i32
println!("{}", my_number); // prints 8
let my_number = 9.2; // This is an f64. It is not my_number
println!("{}", my_number) // Prints 9.2
}

Here we say that we "shadowed" my_number with a


new "let binding".

19 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

So is the first my_number destroyed? No, but when we


call my_number we now get my_number the f64 . And
because they are in the same scope block, we can't see
the first my_number anymore.

But if they are in different blocks, we can see both. For


example:

So when you shadow a variable, you don't destroy it.


You block it.

fn main() {
let my_number = 8; // This is an i32
println!("{}", my_number); // prints 8
{
let my_number = 9.2; // This is an f64. It is not my_num
println!("{}", my_number) // Prints 9.2
// But the shadowed my_number
// The first my_number is still
}
println!("{}", my_number); // prints 8
}

So what is the advantage of shadowing? Shadowing is


good when you need to change a variable a lot.

fn main() {
let final_number = {
let y = 10;
let x = 9; // x starts at 9
let x = times_two(x); // shadow with new x: 18
let x = x + y; // shadow with new x: 28
x // return x: final_number is now the value of x
};
println!("The number is now: {}", final_number)
}

fn times_two(number: i32) -> i32 {


number * 2
}

20 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Without shadowing you would have to think of different


names, even though you don't care about x:

fn main() {
// Pretending we are using Rust without shadowing
let final_number = {
let y = 10;
let x = 9; // x starts at 9
let x_twice = times_two(x); // second name for x
let x_twice_and_y = x + y; // third name for x
x_twice_and_y // too bad we didn't have shadowing - we c
};
println!("The number is now: {}", final_number)
}

fn times_two(number: i32) -> i32 {


number * 2
}

The stack, the heap, and


pointers
The stack, the heap, and pointers are very important in
Rust.

The stack and the heap are two places to keep memory.
The important differences are:

The stack is very fast, but the heap is not so fast.


The stack needs to know the size of a variable at
compile time. So simple variables like i32 go on
the stack, because we know their exact size.
Some types don't know the size at compile time.
But the stack needs to know the exact size. What
do you do? You put the data in the heap, because
the heap can have any size of data. And to find it, a
pointer goes on the stack, because we always

21 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

know the size of a pointer.

A pointer is like a table of contents in a book.

MY BOOK

Chapter Page
Chapter 1: My life 1
Chapter 2: My cat 15
Chapter 3: My job 23
Chapter 4: My family 30
Chapter 5: Future plans 43

So this is like five pointers. Where is the chapter "My


life"? It's on page 1 (it points to page 1). Where is the
chapter "My job?" It's on page 23.

The pointer you usually see in Rust is called a


reference. This is the important part to know: a
reference points to the memory of another value. A
reference means you borrow the value, but you don't
own it. In Rust, references have a & . So:

let my_variable = 8 makes a regular variable,


but
let my_reference = &my_variable makes a
reference.

This means that my_reference is only looking at the


data of my_variable . my_variable still owns its data.

More about printing

We know that println! can print with {} (for Display)


and {:?} (for Debug), plus {:#?} for pretty printing.
But there are many other ways to print.

22 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

For example, if you have a reference, you can use


{:p} to print the pointer address. Pointer address
means the location in your computer's memory.

fn main() {
let number = 9;
let number_ref = &number;
println!("{:p}", number_ref);
}

This prints 0xe2bc0ffcfc or some other address. It


might be different every time, depending on where your
computer stores it.

Or you can print binary, hexadecimal and octal:

fn main() {
let number = 555;
println!("Binary: {:b}, hexadecimal: {:x}, octal: {:o}"
}

Or you can add numbers to change the order:

fn main() {
let father_name = "Vlad";
let son_name = "Adrian Fahrenheit";
let family_name = "Țepeș";
println!("This is {1} {2}, son of {0} {2}."
}

father_name is in position 0, son_name is in position


1, and family_name is in position 2. So it prints This
is Adrian Fahrenheit Țepeș, son of Vlad Țepeș .

Maybe you have a very complex string to print and want


to add names to the {} . You can do that:

23 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn main() {
println!("{city1} is in {country} and {city2} is also in {co
but {city3} is not in {country}.", city1 = "Seoul"
}

Very complex printing is not used too much in Rust. But


here is how to do it.

{variable:padding alignment minimum.maximum}

1. Do you want a variable name? (Then add a : )


2. Do you want a padding character?
3. What alignment (left / middle / right) or the
padding?
4. Do you want a minimum length? (just write a
number)
5. Do you want a maximum length? (write a number
with a . in front)



For example, if I want to write "a" with five
characters on the left and five characters on the
right:

fn main() {

}
let letter = "a";
println!("{: ㅎ
^11}", letter);

This prints ㅎㅎㅎㅎㅎ ㅎㅎㅎㅎㅎ a . Let's


look at 1) to 5) for this.


Do you want a variable name? {: ^11} No


variable name: it comes before : .


Do you want a padding character? {: ^11} Yes.
comes after the : and has a ^ . < means
padding with the character on the left, > means on

24 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust


the right, and ^ means in the middle.
Do you want a minimum length? {: ^11} Yes:


there is an 11 after.
Do you want a maximum length? {: ^11} No:
there is no number with a . before.

Here is an example of many types of formatting.

fn main() {
let title = "TODAY'S NEWS";
println!("{:-^30}", title); // no variable name, pad with -,
let bar = "|";
println!("{: <15}{: >15}", bar, bar); // no variable name, p
let a = "SEOUL";
let b = "TOKYO";
println!("{city1:-<15}{city2:->15}", city1
}

It prints:

---------TODAY'S NEWS---------
| |
SEOUL--------------------TOKYO

Strings
Rust has two main types of strings: String and &str .
What is the difference?

&str is a simple string. When you write let


my_variable = "Hello, world!" , you create a
&str . A &str is very fast.

String is a more complicated string. It is a bit


slower, and has more functions. A String is a
pointer, with data on the heap.

25 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Both &str and String are UTF-8. For example, you


can write:

fn main() {
let name = " 서태지
"; // This is a Korean name. No problem,
let other_name = String::from("Adrian Fahrenheit Țepeș"
}

You can even write emojis thanks to UTF-8.

fn main() {
�";
let name = "�
println!("My name is actually {}", name);
}

So why do we need a & in front of str , but not for


String ?

str is a dynamically sized type (dynamically sized

서태지
= the size can be different). For example, the
names " " and "Adrian Fahrenheit Țepeș"
are not the same size on the stack:

fn main() {

println!("A String is always {:?} bytes. It is Sized."


println!("And an i8 is always {:?} bytes. It is Sized."
println!("And an f64 is always {:?} bytes. It is Sized."
println!("But a &str? It can be anything. '
println!("And 'Adrian Fahrenheit Țepeș' is {:?} bytes. It is
}

That is why we need a &, because & makes a pointer,


and Rust knows the size of the pointer. So the pointer
goes on the stack. If we wrote str , Rust wouldn't know
what to do because it doesn't know the size.

26 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

There are many ways to make a String . Here are


some:

String::from("This is the string text");


This a method for String that takes text and creates
a String.
"This is the string text".to_string() . This
is a method for &str that makes it a String.
The format! macro. This is like println! except
it creates a String instead of printing. So you can do
this:

fn main() {
let my_name = "Billybrobby";
let my_country = "USA";
let my_home = "Korea";

let together = format!(


"I am {} and I come from {} but I live in {}."
my_name, my_country, my_home
);
}

Now we have a string named together but did not print it


yet.

One other way to make a String is called .into() but


it is a bit different. Some types can easily convert to and
from another type using From and .into() . And if
you have From , then you also have .into() . From is
clearer because you already know the types: you know
that String::from("Some str") is a String from a
&str . But with .into() , sometimes the compiler
doesn't know:

fn main() {
let my_string = "Try to make this a String"

27 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Rust doesn't know what type you want, because many


types can be made from a &str .

error[E0282]: type annotations needed


--> src\main.rs:2:9
|
2 | let my_string = "Try to make this a
String".into();
| ^^^^^^^^^ consider giving
`my_string` a type

So you can do this:

fn main() {
let my_string: String = "Try to make this a String"
}

And now you get a String.

const and static


There are two types that don't use let to declare:
const and static . Also, you need to write the type
for them. These are for variables that don't change
( const means constant). The difference is that:

const is a value that does not change,

static is a value that does not change and has a


fixed memory location.

So they are almost the same. Rust programmers almost


always use const .

You write them with ALL CAPITAL LETTERS, and


usually outside of the main function.

28 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Two examples are: const NUMBER_OF_MONTHS: u32 =


12; and const SEASONS: [&str; 4] = ["Spring",
"Summer", "Fall", "Winter"];

More on references
References are very important in Rust. Rust uses
references to make sure that all memory access is safe.
We know that we use & to create a reference:

fn main() {
let country = String::from("Austria");
let ref_one = &country;
let ref_two = &country;

println!("{}", ref_one);
}

country is a String . We created two references to


country . They have the type &String : a "reference to
a String". We could create one hundred references to
country and it would be no problem.

But this is a problem:

fn main() {
let country = return_str();
}

fn return_str() -> &str {


let country = String::from("Austria");
let country_ref = &country;
country_ref
}

29 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

The function return_str() creates a String, then it


creates a reference to the string. Then it tries to return
the reference. But country only lives inside the
function. So after the function is over, country_ref is
referring to memory that is already gone. Rust prevents
us from making a mistake with memory.

Mutable references
If you want to use a reference to change data, you can
use a mutable reference. For a mutable reference, you
write &mut .

fn main() {
let mut my_number = 8; // don't forget to write mut here!
let num_ref = &mut my_number;
}

So what are the two types? my_number is an i32 , and


num_ref is &mut i32 (a "mutable reference to an
i32 ").

So let's use it to add 10 to my_number. But you can't


write num_ref += 10 , because num_ref is not the
i32 value. To reach the value, we use * . * means "I
don't want the reference, I want the value behind the
reference". In other words, one * erases one & .

fn main() {
let mut my_number = 8;
let num_ref = &mut my_number;
*num_ref += 10; // Use * to change the i32 value.
println!("{}", my_number);
}

30 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Because using & is called "referencing", using * is


called "dereferencing".

Rust has rules for mutable and immutable references.

Rule 1: If you have only immutable references, you can


have as many as you want. Rule 2: If you have a
mutable references, you can only have one. You can't
have an immutable reference and a mutable reference.

This is because mutable references can change the


data. You could get problems if you change the data
when other references are reading it.

A good way to understand is to think of a Powerpoint


presentation.

Situation one is about only one mutable reference.

Situation one: An employee is writing a Powerpoint


presentation. He wants his manager to help him. The
employee gives his login information to his manager,
and asks him to help by making edits. Now the manager
has a "mutable reference" to the employee's
presentation. This is fine, because nobody else is
looking at the presentation.

Situation two is about only immutable references.

Situation two: The employee is giving the presentation


to 100 people. All 100 people can now see the
employee's data. This is fine, because nobody can
change the data.

Situation three is the problem situation.

31 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Situation three: The Employee his manager his login


information. Then the employee went to give the
presentation to 100 people. This is not fine, because the
manager can log in and do anything. Maybe his
manager will log into the computer and type an email to
his mother? Now the 100 people have to watch the
manager write an email to his mother instead of the
presentation. That's not what they expected to see.

Here is an example of a mutable borrow with an


immutable borrow:

fn main() {
let mut number = 10;
let number_ref = &number;
let number_change = &mut number;
*number_change += 10;
println!("{}", number_ref);
}

The compiler prints a helpful message to show us the


problem.

error[E0502]: cannot borrow `number` as


mutable because it is also borrowed as
immutable
--> src\main.rs:4:25
|
3 | let number_ref = &number;
| ------- immutable
borrow occurs here
4 | let number_change = &mut number;
| ^^^^^^^^^^^
mutable borrow occurs here
5 | *number_change += 10;
6 | println!("{}", number_ref);
| ---------- immutable
borrow later used here

32 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

However, this code will work. Why?

fn main() {
let mut number = 10;
let number_change = &mut number; // create a mutable referen
*number_change += 10; // use mutable reference to add 10
let number_ref = &number; // create an immutable reference
println!("{}", number_ref); // print the immutable reference
}

The compiler knows that we used number_change to


change number , but didn't use it again. So here there
is no problem. We are not using immutable and mutable
references together.

Shadowing again

Remember when we said that shadowing doesn't


destroy a value but blocks it? Now we can use
references to see this.

fn main() {
let country = String::from("Austria");
let country_ref = &country;
let country = 8;
println!("{}, {}", country_ref, country);
}

Does this print Austria, 8 or 8, 8 ? It prints


Austria, 8 . First we declare a String called
country . Then we create a reference country_ref to
this string. Then we shadow country with 8, which is an
i32 . But the first country was not destroyed, so
country_ref still says "Austria", not "8".

Giving references to

33 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

functions
References are very useful for functions. The rule in
Rust on variables is: a variable can only have one
owner.

This code will not work:

fn main() {
let country = String::from("Austria");
print_country(country); // We print "Austria"
print_country(country); // That was fun, let's do it again!
}

fn print_country(country_name: String) {
println!("{}", country_name);
}

It does not work because country is destroyed. Here's


how:

Step 1: We create the String country .


country is the owner.

Step 2: We give country to print_country .


print_country doesn't have an -> , so it doesn't
return anything. After print_country finishes, our
String is now dead.

Step 3: We try to give country to


print_country , but we already did that. We don't
have country to give anymore.

We can fix this by adding & .

fn main() {
let country = String::from("Austria");
print_country(&country); // We print "Austria"
print_country(&country); // That was fun, let's do it again!

34 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn print_country(country_name: &String) {
println!("{}", country_name);
}

Now print_country() is a function that takes a


reference to a String : a &String . Also, we give it a
reference to country by writing &country . This says
"you can look at it, but I will keep it".

Here is an example of a function that uses a mutable


variable.

fn main() {
let mut country = String::from("Austria");
add_hungary(&mut country);
}

fn add_hungary(country_name: &mut String) {


country_name.push_str("-Hungary"); // push_str() adds a &str
println!("Now it says: {}", country_name);
}

So to conclude:

fn function_name(variable: String) takes a


String and owns it. If it doesn't return anything,
then the variable dies after the function is done.
fn function_name(variable: &String) borrows
a String and can look at it
fn function_name(variable: &mut String)
borrows a String and can change it

Here is an example that looks like a mutable reference,


but it is different.

fn main() {

35 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

let country = String::from("Austria"); // country is not mut


adds_hungary(country);
}

fn adds_hungary(mut country: String) { // but adds_hungary takes


country.push_str("-Hungary");
println!("{}", country);
}

How is this possible? It is because mut hungary is not


a reference: adds_hungary owns country now.
(Remember, it takes String and not &String ).
adds_hungary is the full owner, so it can take
country as mutable.

Copy types
Some types in Rust are very simple. They are called
copy types. These simple types are all on the stack,
and the compiler knows their size. That means that they
are very easy to copy, so the compiler always copies
when you send it to a function. So you don't need to
worry about ownership.

These simple types include: integers, floats, booleans


(true and false), and char.

How do you know if a type implements copy?


(implements = can use) You can check the
documentation. For example, here is the documentation
for char:

https://doc.rust-lang.org/std/primitive.char.html

On the left you can see Trait Implementations. You


can see for example Copy, Debug, and Display. So
you know that a char :

36 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

is copied when you send it to a function (Copy)


can use {} to print (Display)
can use {:?} to print (Debug)

fn main() {
let my_number = 8;
prints_number(my_number); // Prints 8. prints_number gets a
prints_number(my_number); // Prints 8 again.
// No problem, because my_number i
}

prints_number(number: i32) { // No return with ->


// If number was not copy type, it
// and we couldn't use it again
println!("{}", number);
}

But if you look at the documentation for String, it is not


copy type.

https://doc.rust-lang.org/std/string/struct.String.html

On the left in Trait Implementations you can look in


alphabetical order. A, B, C... there is no Copy in C. But
there is Clone. Clone is similar to Copy, but needs
more memory.

In this example, prints_country() prints the country


name, a String . We want to print it two times, but we
can't:

fn main() {
let country = String::from("Kiribati");
prints_country(country);
prints_country(country);
}

fn prints_country(country_name: String) {
println!("{}", country_name);

37 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

But now we understand the message.

error[E0382]: use of moved value: `country`


--> src\main.rs:4:20
|
2 | let country =
String::from("Kiribati");
| ------- move occurs because
`country` has type `std::string::String`,
which does not implement the `Copy` trait
3 | prints_country(country);
| ------- value moved
here
4 | prints_country(country);
| ^^^^^^^ value used
here after move

The important part is which does not implement the


Copy trait . But in the documentation we saw that
String implements the Clone trait. So we can add
.clone() . This creates a clone, and we send the clone
to the function. country is still alive, so we can use it.

fn main() {
let country = String::from("Kiribati");
prints_country(country.clone()); // make a clone and give it
prints_country(country);
}

fn prints_country(country_name: String) {
println!("{}", country_name);
}

38 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Of course, if the String is very large, .clone() can


use a lot of memory. For example, one String can be
a whole book, and every time we call .clone() it will
copy the book. So using & for a reference is faster, if
you can.

fn main() {
let mut country = String::from("Kiribati");
let country_ref = &country; // country_ref needs a reference
changes_country(&mut country); // changes_country needs a &m
println!("{}", country_ref); // immutable and mutable borrow
}

fn prints_country(country_name: String) {
println!("{}", country_name);
}

fn changes_country(country_name: &mut String) {


country_name.push_str(" is a country");
println!("{}", country_name);
}

Maybe we can change the order, but we can also just


.clone() .

fn main() {
let mut country = String::from("Kiribati");
let country_ref = &country; // country_ref needs a reference
changes_country(&mut country.clone()); // give changes_count
println!("{}", country_ref); // now the code works
}

fn prints_country(country_name: String) {
println!("{}", country_name);
}

fn changes_country(country_name: &mut String) {


country_name.push_str(" is a country");
println!("{}", country_name);
}

39 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Variables without values

A variable without a value is called an "uninitialized"


variable. Uninitialized means "hasn't started yet". They
are simple, just let and the name:

let my_variable;

But you can't use it yet. Your program won't compile if it


tries to use it.

But sometimes they can be useful. A good example is


when:

You have a code block and inside that is the value


for your variable, and
The variable needs to live outside of the code
block.

fn loop_then_return(mut counter: i32) -> i32 {


loop {
counter += 1;
if counter % 50 == 0 {
break;
}
}
counter
}

fn main() {
let my_number;

{
// Pretend we need to have this code block
let number = {
// Pretend there is code here to make a number
7
};

40 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

my_number = loop_then_return(number);
}

println!("{}", my_number);
}

It is important to know that my_number was declared in


the main() function, so it lives until the end. But it gets
its value from inside a loop. However, that value lives as
long as my_number , because my_number has the
value.

It helps to imagine if you simplify the code.


loop_then_return(number) gives the result 50, so
let's delete it and write 50 instead. Also, now we don't
need number so we will delete it too. Now it looks like
this:

fn main() {
let my_number;
{
my_number = 50;
}

println!("{}", my_number);
}

So it's almost like saying let my_number = { 50 }; .

Also note that my_number is not mut . We didn't give it


a value until we gave it 50, so it never changed its
value.

Collection types
Here are some types for making a collection.

41 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Arrays

An array is data inside square brackets: [] . Arrays:

must not change their size,


must only contain the same type.

They are very fast, however.

The type of an array is: [type; number] . For example,


the type of ["One", "Two"] is [&str; 2] . This
means that even these two arrays have different types:

let array1 = ["One", "Two"];


let array2 = ["One", "Two", "Five"];

A good tip: to know the type of a variable, you can "ask"


the compiler by giving it bad instructions. For example:

fn main() {
let seasons = ["Spring", "Summer", "Autumn"
let seasons2 = ["Spring", "Summer", "Fall",
let () = seasons; // This will make an error
let () = seasons2; // This will also make an error
}

The compiler says "seasons isn't type () and


seasons2 isn't type () either!" as you can see:

error[E0308]: mismatched types


--> src\main.rs:4:9
|
4 | let () = seasons;
| ^^ ------- this expression has
type `[&str; 4]`
| |
| expected array `[&str; 4]`,

42 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

found `()`

error[E0308]: mismatched types


--> src\main.rs:5:9
|
5 | let () = seasons2;
| ^^ -------- this expression
has type `[&str; 5]`
| |
| expected array `[&str; 5]`,
found `()`

If you want an array with all the same value, you can
declare it like this:

fn main() {
let my_array = ["a"; 20];
println!("{:?}", my_array);
}

This prints ["a", "a", "a", "a", "a", "a", "a",


"a", "a", "a", "a", "a", "a", "a", "a", "a",
"a", "a", "a", "a"] .

This method is used a lot to create buffers. For


example, let mut buffer = [0; 640] creates an array
of 640 zeroes. Then we can change zero to other
numbers in order to add data.

You can index (get) entries in an array with []. The first
entry is [0], the second is [1], and so on.

fn main() {
let my_numbers = [0, 10, -20];
println!("{}", my_numbers[1]); // prints 10
}

43 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

You can get a slice (a piece) of an array. First you need


a &, because the compiler doesn't know the size. Then
you can use .. to show the range.

For example, let's use this array: [1, 2, 3, 4, 5, 6,


7, 8, 9, 10] .

fn main() {
let array_of_ten = [1, 2, 3, 4, 5, 6, 7, 8,

let three_to_five = &array_of_ten[2..5];


let start_at_two = &array_of_ten[1..];
let end_at_five = &array_of_ten[..5];
let everything = &array_of_ten[..];

println!("Three to five: {:?},


start at two: {:?}
end at five: {:?}
everything: {:?}", three_to_five, start_at_two, end_at_five, every
}

Remember that:

Index numbers start at 0 (not 1)


Index ranges are exclusive (they do not include the
last number)

So [0..2] means the first index and the second index


(0 and 1). Or you can call it the "zeroth and first" index.
It doesn't have the third item, which is index 2.

You can also have an inclusive range, which means it


includes the last number too. To do this, add = to write
..= instead of .. . So instead of [0..2] you can
write [0..=2] if you want the first, second, and third
item.

Vectors

44 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

In the same way that we have &str and String , we


have arrays and vectors. Arrays are faster with less
functionality, and vectors are slower with more
functionality. The type is written Vec .

There are two main ways to declare a vector. One is like


with String using new :

fn main() {
let name1 = String::from("Windy");
let name2 = String::from("Gomesy");

let mut my_vec = Vec::new();


// If we run the program now, the compiler will give an erro
// It doesn't know the type of vec.

my_vec.push(name1); // Now it knows: it's Vec<String>


my_vec.push(name2);
}

Or you can just declare the type.

fn main() {
let mut my_vec: Vec<String> = Vec::new(); // The compiler kn
// so there is no
}

You can see that items in vectors must have the same
type.

Another easy way to create a vector is with the vec!


macro. It looks like an array declaration, but has vec!
in front of it.

fn main() {
let mut my_vec = vec![8, 10, 10];
}

45 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

The type is Vec<i32> . You call it a "vec of i32s". And a


Vec<String> is a "vec of strings". And a
Vec<Vec<String>> is a "vec of a vec of strings".

You can slice a vector too, just like in an array.

fn main() {
let vec_of_ten = vec![1, 2, 3, 4, 5, 6, 7,
// Everything is the same except we added vec!
let three_to_five = &vec_of_ten[2..5];
let start_at_two = &vec_of_ten[1..];
let end_at_five = &vec_of_ten[..5];
let everything = &vec_of_ten[..];

println!("Three to five: {:?},


start at two: {:?}
end at five: {:?}
everything: {:?}", three_to_five, start_at_two, end_at_five, every
}

Because a vec is slower than an array, we can use


some methods to make it faster. A vec has a capacity,
which means the space given to the vector. If you add
more to a vector than its capacity, it will make its
capacity double and copy the items into the new space.
This is called reallocation.

For example:

fn main() {
let mut num_vec = Vec::new();
num_vec.push('a'); // add one character
println!("{}", num_vec.capacity()); // prints 1
num_vec.push('a'); // add one more
println!("{}", num_vec.capacity()); // prints 2
num_vec.push('a'); // add one more
println!("{}", num_vec.capacity()); // prints 4. It has thre
num_vec.push('a'); // add one more
num_vec.push('a'); // add one more // Now we have 5 elements
println!("{}", num_vec.capacity()); // Now capacity is 8

46 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

So this vector has three reallocations: 1 to 2, 2 to 4, and


4 to 8. We can make it faster:

fn main() {
let mut num_vec = Vec::with_capacity(8); // Give it capacity
num_vec.push('a'); // add one character
println!("{}", num_vec.capacity()); // prints 8
num_vec.push('a'); // add one more
println!("{}", num_vec.capacity()); // prints 8
num_vec.push('a'); // add one more
println!("{}", num_vec.capacity()); // prints 8.
num_vec.push('a'); // add one more
num_vec.push('a'); // add one more // Now we have 5 elements
println!("{}", num_vec.capacity()); // Still 8
}

This vector has 0 reallocations, which is better. So if you


think you know how many elements you need, you can
use Vec::with_capacity() to make it faster.

You remember that you can use .into() to make a


&str into a String . You can also use it to make an
array into a Vec . You have to tell .into() that you
want a Vec , but you don't have to choose the type of
Vec . If you don't want to choose, you can write
Vec<_> .

fn main() {
let my_vec: Vec<u8> = [1, 2, 3].into();
let my_vec2: Vec<_> = [9, 0, 10].into(); // Vec<_> means "ch
// Rust will choose
}

Tuples

47 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Tuples in Rust use () . We have seen many empty


tuples already. fn do_something() {} has an empty
tuple. Also, when you don't return anything in a function,
you actually return an empty tuple.

fn main() {

fn just_prints() {
println!("I am printing"); // Adding ; means we return an em
}

But tuples can hold many things, and can hold different
types too. To access the items inside of a tuple, don't
use [] , use . .

fn main() {
let mut new_vec = Vec::new();
new_vec.push('a');
let random_tuple = ("Here is a name", 8, new_vec,
println!(
"Inside the tuple is: First item: {:?}
Second item: {:?}
Third item: {:?}
Fourth item: {:?}
Fifth item: {:?}
Sixth item: {:?}",
random_tuple.0,
random_tuple.1,
random_tuple.2,
random_tuple.3,
random_tuple.4,
random_tuple.5,
)
}

You can use a tuple to create multiple variables.

48 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn main() {
let str_vec = vec!["one", "two", "three"];

let (a, b, c) = (str_vec[0], str_vec[1], str_vec[


println!("{:?}", b);
}

There are many more collection types, and many more


ways to use arrays, vecs, and tuples. We will learn more
about them. But first we will learn control flow.

Control flow
Control flow means telling your code what to do in
different situations. The simplest control flow is if .

fn main() {
let my_number = 5;
if my_number == 7 {
println!("It's seven");
}
}

Please note that we wrote if my_number == 7 and not


if (my_number == 7) . You don't need () with if in
Rust.

else if and else gives you more control:

fn main() {
let my_number = 5;
if my_number == 7 {
println!("It's seven");
} else if my_number == 6 {
println!("It's six")
} else {
println!("It's a different number")

49 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

}
}

You can add more conditions with && (and) and ||


(or).

Too much if , else , and else if can be difficult to


read. You can use match instead. But you must match
for every possible result. For example, this will not work:

fn main() {
let my_number: u8 = 5;
match my_number {
0 => println!("it's zero"),
1 => println!("it's one"),
2 => println!("it's two"),
}
}

The compiler says:

error[E0004]: non-exhaustive patterns:


`3u8..=std::u8::MAX` not covered
--> src\main.rs:3:11
|
3 | match my_number {
| ^^^^^^^^^ pattern
`3u8..=std::u8::MAX` not covered

This means "you told me about 0 to 2, but u8s can go


up to 255. What about 3? What about 4? What about
5?" And so on. So you can add _ which means
"anything else".

fn main() {
let my_number: u8 = 5;
match my_number {
0 => println!("it's zero"),

50 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

1 => println!("it's one"),


2 => println!("it's two"),
_ => println!("It's some other number"),
}
}

Remember this for match:

You write match and then make a {} code block.


Write the pattern on the left and use a => fat arrow
to say what to do when it matches.
Each line is called an "arm".
Put a comma between the arms (not a semicolon).

You can declare a value with a match:

fn main() {
let my_number = 5;
let second_number = match my_number {
0 => 0,
5 => 10,
_ => 2,
};
}

Do you see the semicolon at the end? That is because,


after the match is over, we actually told the compiler
this: let second_number = 10;

You can match on more complicated things too. You use


a tuple to do it.

fn main() {
let sky = "cloudy";
let temperature = "warm";

match (sky, temperature) {


("cloudy", "cold") => println!("It's dark and unpleasant

51 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

("clear", "warm") => println!("It's a nice day"


("cloudy", "warm") => println!("It's dark but not bad"
_ => println!("Not sure what the weather is."
}
}

You can even put if inside of match .

fn main() {
let children = 5;
let married = true;

match (children, married) {


(children, married) if married == false
(children, married) if children == 0 &&
_ => println!("Married? {}. Number of children: {}."
}
}

You can use _ as many times as you want in a match.


In this match on colours, we have three but only check
one at a time.

fn match_colours(rbg: (i32, i32, i32)) {


match rbg {
(r, _, _) if r < 10 => println!("Not much red"
(_, b, _) if b < 10 => println!("Not much blue"
(_, _, g) if g < 10 => println!("Not much green"
_ => println!("Each colour has at least 10"
}
}

fn main() {
let first = (200, 0, 0);
let second = (50, 50, 50);
let third = (200, 50, 0);

match_colours(first);
match_colours(second);
match_colours(third);

52 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

This prints:

Not much blue


Each colour has at least 10
Not much green

This also shows how match statements work, because


in the first example it only printed Not much blue . But
first also has not much green. A match statement
always stops when it finds a match, and doesn't check
the rest. This is a good example of code that compiles
well but is not the code you want. You can make a really
big match statement to fix it, but it is probably better to
use a for loop. We will talk about loops soon.

A match has to return the same type. So you can't do


this:

fn main() {
let my_number = 10;
let some_variable = match my_number {
10 => 8,
_ => "Not ten",
};
}

The compiler tells you that:

error[E0308]: `match` arms have incompatible


types
--> src\main.rs:17:14
|
15 | let some_variable = match
my_number {

53 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

| _________________________-
16 | | 10 => 8,
| | - this is found to be
of type `{integer}`
17 | | _ => "Not ten",
| | ^^^^^^^^^ expected
integer, found `&str`
18 | | };
| |_____- `match` arms have incompatible
types

This will also not work, for the same reason:

let some_variable = if my_number == 10 { 8 } else

But this works, because you have a different let


statement.

fn main() {
let my_number = 10;

if my_number == 10 {
let some_variable = 8;
} else {
let some_variable = "Something else";
}
}

You can also use @ to use the value of a match


expression when you want to. In this example we match
an i32 input in a function. If it's 4 or 13 we want to use
that number in a println! statement. Otherwise, we
don't need to use it.

fn match_number(input: i32) {
match input {
number @ 4 => println!("{} is an unlucky number in China (so
number @ 13 => println!("{} is unlucky in North America, luc

54 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

_ => println!("Looks like a normal number"),


}
}

fn main() {
match_number(50);
match_number(13);
match_number(4);
}

Structs
With structs, you can create your own type. Structs are
created with the keyword struct . The name of a struct
should be in UpperCamelCase (capital letter for each
word, no spaces).

There are three types of structs. One is a "unit struct".


Unit means "doesn't have anything".

struct FileDirectory;

The next is a tuple struct, or an unnamed struct. It is


"unnamed" because you only need to write the types,
not the variable names. Tuple structs are good when
you need a simple struct and don't need to remember
names.

struct Colour(u8, u8, u8);

fn main() {
let my_colour = Colour(50, 0, 50); // Make a colour out of R
println!("The second part of the colour is: {}"
}

55 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

The third type is the named struct. This is probably the


most common struct. In this struct you declare variable
names and types inside a {} code block.

struct Colour(u8, u8, u8); // Declare the same Colour tuple stru

struct SizeAndColour {
size: u32,
colour: Colour, // And we put it in our new named struct
}

fn main() {
let my_colour = Colour(50, 0, 50);

let size_and_colour = SizeAndColour {


size: 150,
colour: my_colour
};
}

Let's create a Country struct to give an example. The


Country struct has the fields population , capital ,
and leader_name .

struct Country {
population: u32,
capital: String,
leader_name: String
}

fn main() {
let population = 500_000;
let capital = String::from("Elist");
let leader_name = String::from("Batu Khasikov"

let kalmykia = Country {


population: population,
capital: capital,
leader_name: leader_name,
};

56 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Did you notice that we wrote the same thing twice?


Actually, you don't need to do that. If the field name and
variable name are the same, you don't have to write it
twice.

struct Country {
population: u32,
capital: String,
leader_name: String
}

fn main() {
let population = 500_000;
let capital = String::from("Elist");
let leader_name = String::from("Batu Khasikov"

let kalmykia = Country {


population,
capital,
leader_name,
};
}

Enums
An enum is short for enumerations. They look similar to
a struct, but are different. Here is the difference:

Use a struct when you want one thing AND another


thing.
Use an enum when you want one thing OR another
thing.

So structs are for many things together, while enums


are for many choices together.

57 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

To declare an enum, write enum and use a code block


with the options, separated by commas. We will create
an enum called SkyState :

enum ThingsInTheSky {
Sun,
Stars,
}

This is an enum because you can either see the sun, or


the stars: you have to choose one. These are called
variants.

// create the enum with two choices


enum ThingsInTheSky {
Sun,
Stars,
}

// With this function we can use an i32 to create ThingsInTheSky


fn create_skystate(time: i32) -> ThingsInTheSky {
match time {
6..=18 => ThingsInTheSky::Sun, // Between 6 and 18 hours
_ => ThingsInTheSky::Stars, // Otherwise, we can see sta
}
}

// With this function we can match against the two choices in Th


fn check_skystate(state: &ThingsInTheSky) {
match state {
ThingsInTheSky::Sun => println!("I can see the sun!"
ThingsInTheSky::Stars => println!("I can see the stars!"
}
}

fn main() {
let time = 8; // it's 8 o'clock
let skystate = create_skystate(time); // create_skystate ret
check_skystate(&skystate); // Give it a reference so it can
}

58 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

You can add data to an enum too.

enum ThingsInTheSky {
Sun(String), // Now each variant has a string
Stars(String),
}

fn create_skystate(time: i32) -> ThingsInTheSky {


match time {
6..=18 => ThingsInTheSky::Sun(String::from
_ => ThingsInTheSky::Stars(String::from
}
}

fn check_skystate(state: &ThingsInTheSky) {
match state {
ThingsInTheSky::Sun(description) => println!
ThingsInTheSky::Stars(n) => println!("{}"
}
}

fn main() {
let time = 8; // it's 8 o'clock
let skystate = create_skystate(time); // create_skystate ret
check_skystate(&skystate); // Give it a reference so it can
}

Enums to use multiple types

You know that items in a Vec , array, etc. all need the
same type (only tuples are different). But you can
actually use an enum to put different types in. Imagine
we want to have a Vec with u32 s or i32 s. Of course,
you can make a Vec<(u32, i32)> (a vec with (u32,
i32) tuples) but we only want one. So here you can
use an enum. Here is a simple example:

enum Number {
U32(u32),

59 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

I32(i32),
}

So there are two variants: the U32 variant with a u32


inside, and the I32 variant with i32 inside. U32 and
I32 are just names we made. They could have been
UThirtyTwo or IThirtyTwo or anything else.

Now, if we put them into a Vec we just have a


Vec<Number> , and the compiler is happy. Because it's
an enum, you have to pick one. We will use the
.is_positive() method to pick. If it's true then we
will choose U32 , and if it's false then we will choose
I32 .

Now the code looks like this:

#[derive(Debug)] // So we can print it


enum Number {
U32(u32),
I32(i32),
}

impl Number {
fn new(number: i32) -> Number { // input number is i32
match number.is_positive() {
true => Number::U32(number as u32),
false => Number::I32(number), // otherwise just give
}
}
}

fn main() {
let my_vec = vec![Number::new(-800), Number

for item in my_vec {


println!("{:?}", item);
}

60 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

This prints what we wanted to see:

I32(-800)
U32(8)

References and the dot


operator
We learned that when you have a reference, you need
to use * to get to the value. A reference is a different
type, so this won't work:

fn main() {
let my_number = 9;
let reference = &my_number;

println!("{}", my_number == reference);


}

The compiler prints:

error[E0277]: can't compare `{integer}` with


`&{integer}`
--> src\main.rs:5:30
|
5 | println!("{}", my_number ==
reference);
| ^^ no
implementation for `{integer} == &{integer}`

So we change line 5 to println!("{}", my_number ==


*reference); and now it prints true . This is called
dereferencing.

But when you use a method, Rust will dereference for


you. The . in a function is called the dot operator.

61 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

First, let's make a struct with one u8 field. Then we will


make a reference to it and try to compare. It will not
work:

struct Item {
number: u8,
}

fn main() {
let item = Item {
number: 8,
};

let reference_number = &item.number; // reference number typ

println!("{}", reference_number == 8); // &u8 and u8 cannot


}

To make it work, we need to dereference: println!


("{}", *reference_number == 8); .

But with the dot operator, we don't need * . For


example:

struct Item {
number: u8,
}

fn main() {
let item = Item {
number: 8,
};

let reference_item = &item;

println!("{}", reference_item.number == 8);


}

62 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Now let's create a method for Item that compares


number to another number. We don't need to use *
anywhere:

struct Item {
number: u8,
}

impl Item {
fn compare_number(&self, other_number: u8) {
println!("Are {} and {} equal? {}", self
// We don't need to write *self.number
}
}

fn main() {
let item = Item {
number: 8,
};

let reference_item = &item; // This is type &Item


let reference_item_two = &reference_item; // This is type &&

item.compare_number(8); // the method works


reference_item.compare_number(8); // it works here too
reference_item_two.compare_number(8); // and here

So just remember: when you use the . operator, you


don't need to worry about * .

Destructuring
You can get the values from a struct or enum by using
let backwards. This is called destructuring , and
gives you the values separately. First a simple example:

struct Person { // make a simple struct for a person

63 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

name: String,
real_name: String,
height: u8,
happiness: bool
}

fn main() {
let papa_doc = Person { // create variable papa_doc
name: "Papa Doc".to_string(),
real_name: "Clarence".to_string(),
height: 170,
happiness: false
};

let Person { // destructure papa_doc


name: a,
real_name: b,
height: c,
happiness: d
} = papa_doc;

println!("Our four values are: {}, {}, {}, and {}."

You can see that it's backwards. First we say let


papa_doc = Person { fields } to create the struct.
Then we say let Person {fields} = papa_doc to
destructure it.

You don't have to write name: a - you can just write


name . But here we write name = a because we want
to use the variable name a .

Now a bigger example. In this example we have a


City struct. We give it a new function to make it. Then
we have a process_city_values function to do things
with the values. In the function we just create a Vec ,
but you can imagine that we can do much more after we
destructure it.

64 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

struct City {
name: String,
name_before: String,
population: u32,
date_founded: u32,
}

impl City {
fn new(name: String, name_before: String, population:
Self {
name,
name_before,
population,
date_founded,
}
}
}

fn main() {
let tallinn = City::new("Tallinn".to_string
process_city_values(&tallinn);
}

fn process_city_values(city: &City) {
let City {
name,
name_before,
population,
date_founded,
} = city;
// now we have the values to use separately
let two_names = vec![name, name_before];
println!("{:?}", two_names);
}

Loops
With loops you can tell Rust to continue something until
you want it to stop. With loop you can start a loop that
does not stop, unless you tell it when to break .

65 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn main() { // This program will never stop


loop {

}
}

So let's tell the compiler when it can break.

fn main() {
let mut counter = 0; // set a counter to 0
loop {
counter +=1; // increase the counter by 1
println!("The counter is now: {}", counter);
if counter == 5 { // stop when counter == 5
break;
}
}
}

This will print:

The counter is now: 1


The counter is now: 2
The counter is now: 3
The counter is now: 4
The counter is now: 5

If you have a loop inside of a loop, you can give them


names. With names, you can tell Rust which loop to
break out of. Use ' (called a "tick") and a : to give
it a name:

fn main() {
let mut counter = 0;
let mut counter2 = 0;
println!("Now entering the first loop.");

'first_loop: loop { // Give the first loop a name

66 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

counter +=1;
println!("The counter is now: {}", counter);
if counter > 9 { // Starts a second loop inside this loo
println!("Now entering the second loop."

'second_loop: loop { // now we are inside `second_lo


println!("The second counter is now: {}"
counter2 +=1;
if counter2 == 3 {
break 'first_loop; // Break out of `first_loop
}
}
}
}
}

A while loop is a loop that continues while something


is still true . Each loop, Rust will check if it is still
true . If it becomes false , Rust will stop the loop.

fn main() {
let mut counter = 0;

while counter < 5 {


counter +=1;
println!("The counter is now: {}", counter);
}
}

A for loop lets you tell Rust what to do each time. But
in a for loop, the loop stops after a certain number of
times. for loops use ranges very often. You use ..
and ..= to create a range.

.. creates an exclusive range: 0..3 creates


0, 1, 2 .

..= creates an inclusive range: 0..=3 = 0, 1,


2, 3 .

67 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn main() {
for number in 0..3 {
println!("The number is: {}", number);
}

for number in 0..=3 {


println!("The next number is: {}", number);
}
}

Also notice that number becomes the variable name for


0..3. We can then use that name in println! .

If you don't need a variable name, use _ .

fn main() {
for _ in 0..3 {
println!("Printing the same thing three times"
}
}

Actually, if you give a variable name and don't use it,


Rust will tell you:

fn main() {
for number in 0..3 {
println!("Printing the same thing three times"
}
}

This is not an error, but Rust will remind you that you
didn't use number :

warning: unused variable: `number`


--> src\main.rs:2:9
|
2 | for number in 0..3 {
| ^^^^^^ help: if this is

68 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

intentional, prefix it with an underscore:


`_number`

Rust also suggests _number . Putting _ in front of a


variable name means "maybe I will use it later". But
using just _ means "I don't care about this variable at
all".

You can also use break to return a value. You write the
value right after break and use a ; . Here is an
example with a loop and a break that gives
my_number its value.

fn main() {
let mut counter = 5;
let my_number = loop {
counter +=1;
if counter % 53 == 3 {
break counter;
}
};
println!("{}", my_number);
}

break counter; means "break with the value of


counter". And because the whole bloock starts with
let , my_number gets the value.

Now that we know how to use loops, here is a better


solution to the match problem with colours. It is a
better solution because we want to compare everything,
and a for loop looks at every item.

fn match_colours(rbg: (i32, i32, i32)) {


let new_vec = vec![(rbg.0, "red"), (rbg.1,
let mut all_have_at_least_10 = true; // Start with true. We
for item in new_vec {
if item.0 < 10 {

69 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

all_have_at_least_10 = false; // Now it's false


println!("Not much {}.", item.1) // And we print the
}
}
if all_have_at_least_10 { // Check if it's still true, and p
println!("Each colour has at least 10."
}
}

fn main() {
let first = (200, 0, 0);
let second = (50, 50, 50);
let third = (200, 50, 0);

match_colours(first);
match_colours(second);
match_colours(third);
}

Implementing structs and


enums
To call functions on a struct or an enum , use an
impl block. These functions are called methods.
There are two kinds of methods in an impl block.

Regular methods: these take self (or &self or


&mut self). Regular methods use a . . .clone()
is a regular method.
Associated methods (or "static" methods): these do
not take self. They are written differently, using :: .
String::from() is an associated method. You
usually use associated methods to create new
variables.

70 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

In our example we are going to create animals and print


them. For a new struct or enum, you need to give it
Debug if you want to use {:?} to print. If you write
#[derive(Debug)] above the struct or enum then you
can print it with {:?} .

#[derive(Debug)]
struct Animal {
age: u8,
animal_type: AnimalType,
}

impl Animal {
fn new() -> Self {
// Self means AnimalType.
//You can also write AnimalType instead of Self

Self {
// When we write Animal::new(), we always get a cat
age: 10,
animal_type: AnimalType::Cat,
}
}

fn change_to_dog(&mut self) {
// use .change_to_dog() to change the cat to a dog
// with &mut self we can change it
println!("Changing animal to dog!");
self.animal_type = AnimalType::Dog;
}

fn change_to_cat(&mut self) {
// use .change_to_dog() to change the cat to a dog
// with &mut self we can change it
println!("Changing animal to cat!");
self.animal_type = AnimalType::Cat;
}

fn check_type(&self) {
// we want to read self
match self.animal_type {
AnimalType::Dog => println!("The animal is a dog"

71 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

AnimalType::Cat => println!("The animal is a cat"


}
}
}

#[derive(Debug)]
enum AnimalType {
Cat,
Dog,
}

fn main() {
let mut new_animal = Animal::new(); // Associated method to
// It is a cat, 10 years
new_animal.check_type();
new_animal.change_to_dog();
new_animal.check_type();
new_animal.change_to_cat();
new_animal.check_type();
}

This prints:

The animal is a cat


Changing animal to dog!
The animal is a dog
Changing animal to cat!
The animal is a cat

Self

Remember that Self (the type Self) and self (the


variable self) are abbreviations. (abbreviation = short
way to write)

So in our code, Self = AnimalType. Also, fn


change_to_dog(&mut self) means fn
change_to_dog(&mut AnimalType)

72 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Generics
In functions, you write what type to take as input:

fn return_number(number: i32) -> i32 {


println!("Here is your number.");
number
}

fn main() {
let number = return_number(5);
}

But maybe you also want to input an i8 , or a u32 ,


and so on. You can use generics for this. Generics
means "maybe one type, maybe another type".

For generics, you use angle brackets with the type


inside: <T> This means "any type you put into the
function". Usually, generics uses types with one capital
letter (T, U, V, etc.).

This is how you change the function:

fn return_number<T>(number: T) -> T {
println!("Here is your number.");
number
}

The important part is the <T> after the function name.


Without this, Rust will think that T is a concrete
(concrete = not generic) type, like String or i8 .

This is easier to understand if we write out a type name:

fn return_number(number: MyType) -> MyType {


println!("Here is your number.");

73 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

number
}

As you can see, MyType is concrete, not generic. So


we need to write this:

fn return_number<MyType>(number: MyType) -> MyType {


println!("Here is your number.");
number
}

So now it works:

fn return_number<MyType>(number: MyType) -> MyType {


println!("Here is your number.");
number
}

fn main() {
let number = return_number(5);
}

Now we will go back to type T , because Rust code


usually uses T .

You will remember that some types in Rust are Copy,


some are Clone, some are Display, some are Debug,
and so on. With Debug, we can print with {} . So now
you can see that this is a problem:

fn print_number<T>(number: T) {
println!("Here is your number: {:?}", number);
}

fn main() {
print_number(5);
}

74 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

print_number needs Debug to print number , but is


T a type with Debug ? Maybe not. The compiler
doesn't know, so it gives an error:

error[E0277]: `T` doesn't implement


`std::fmt::Debug`
--> src\main.rs:29:43
|
29 | println!("Here is your number:
{:?}", number);
|
^^^^^^ `T` cannot be formatted using `{:?}`
because it doesn't implement
`std::fmt::Debug`

T doesn't implement Debug. So do we implement


Debug for T? No, because we don't know what T is. But
we can tell the function: "only accept types that have
Debug".

use std::fmt::Debug; // Debug is located at std::fmt::Debug. If


// then now we can just write 'Debug'.

fn print_number<T: Debug>(number: T) {
println!("Here is your number: {:?}", number);
}

fn main() {
print_number(5);
}

So now the compiler knows: "Okay, I will only take a


type if it has Debug". Now the code works, because
i32 is Debug. Now we can give it many types:
String , &str , and so on.

Now we can create a struct and give it Debug, and now


we can print it too. Our function can take i32 , the
struct Animal, and more:

75 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

use std::fmt::Debug;

#[derive(Debug)]
struct Animal {
name: String,
age: u8,
}

fn print_item<T: Debug>(item: T) {
println!("Here is your item: {:?}", item);
}

fn main() {
let charlie = Animal {
name: "Charlie".to_string(),
age: 1,
};

let number = 55;

print_item(charlie);
print_item(number);
}

This prints:

Here is your item: Animal { name: "Charlie",


age: 1 }
Here is your item: 55

Sometimes we need more than one type in a generic


function. We have to write out each type name, and
think about how we want to use it. In this example, we
want two types. First we want to print a statement for
type T. Printing with {} is nicer, so we will require
Display for T.

76 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Next is type U, and two variables have type U (U is


some sort of number). We want to compare them, so
we need PartialOrd. We want to print them too, so we
require Display for U as well.

use std::fmt::Display;
use std::cmp::PartialOrd;

fn compare_and_display<T: Display, U: Display +


println!("{}! Is {} greater than {}? {}", statement, num_1,
}

fn main() {
compare_and_display("Listen up!", 9, 8);
}

So fn compare_and_display<T: Display, U:
Display + PartialOrd>(statement: T, num_1: U,
num_2: U) says:

The function name is compare_and_display ,


The first type is T, and it is generic. It must be a
type that can print with {}.
The next type is U, and it is generic. It must be a
type that can print with {}. Also, it must be a type
that can compare (use > , < , and == ).

Now we can give compare_and_display different


types. statement can be a String , a &str , anything
with Display.

To make generic functions easier to read, we can also


write it like this:

use std::cmp::PartialOrd;
use std::fmt::Display;

fn compare_and_display<T, U>(statement: T, num_1: U, num_2: U)

77 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

where
T: Display,
U: Display + PartialOrd,
{
println!("{}! Is {} greater than {}? {}", statement, num_1,
}

fn main() {
compare_and_display("Listen up!", 9, 8);
}

Using where is a good idea when you have many


generic types.

Also note:

If you have one type T and another type T, they


must be the same.
If you have one type T and another type U, they
can be different. But they can also be the same.

For example:

use std::fmt::Display;

fn say_two<T: Display, U: Display>(statement_1: T, statement_2:


println!("I have two things to say: {} and {}"
}

fn main() {

say_two("Hello there!", String::from("I hate sand."


say_two(String::from("Where is Padme?"), String
}

Option and Result

78 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

We understand enums and generics now, so we can


understand Option and Result . Rust uses these two
enums to make code safer. We will start with Option.

You use Option when you have a value that might exist,
or might not exist. Here is an example of bad code that
can be improved with Option.

fn main() {
let new_vec = vec![1, 2];
let index = take_fifth(new_vec);
}

fn take_fifth(value: Vec<i32>) -> i32 {


value[4]
}

When we run the code, it panics. Here is the message:

thread 'main' panicked at 'index out of


bounds: the len is 2 but the index is 4',
src\main.rs:34:5

Panic means that the program stops before the problem


happens. Rust sees that the function wants something
impossible, and stops. It "unwinds the stack" (takes the
values off the stack) and tells you "sorry, I can't do that".

So now we will change the return type from i32 to


Option<i32> . This means "give me an i32 if it's
there, and give me None if it's not". We say that the
i32 is "wrapped" in an Option, which means that it's
inside an Option.

fn main() {
let new_vec = vec![1, 2];
let bigger_vec = vec![1, 2, 3, 4, 5];

79 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

println!("{:?}, {:?}", take_fifth(new_vec),


}

fn take_fifth(value: Vec<i32>) -> Option<i32> {


if value.len() < 5 { // .len() gives the length of the vec.
// It must be at least 5.
None
} else {
Some(value[4])
}
}

This prints None, Some(5) . This is good, because now


we don't panic anymore. But how do we get the value
5?

We can get the value inside an option with .unwrap() ,


but be careful with .unwrap() . If you unwrap a value
that is None , the program will panic.

fn main() {
let new_vec = vec![1, 2];
let bigger_vec = vec![1, 2, 3, 4, 5];
println!( // with .unwrap() the code is getting longer
// so we will write on more than one line.
"{:?}, {:?}",
take_fifth(new_vec).unwrap(), // this one is None. .unwra
take_fifth(bigger_vec).unwrap()
);
}

fn take_fifth(value: Vec<i32>) -> Option<i32> {


if value.len() < 4 {
None
} else {
Some(value[4])
}
}

80 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

But we don't need to use .unwrap() . We can use a


match , and print when we have Some , and not print if
we have None . For example:

fn main() {
let new_vec = vec![1, 2];
let bigger_vec = vec![1, 2, 3, 4, 5];
let mut option_vec = Vec::new(); // Make a new vec to hold o
// The vec is type: Vec<Opt

option_vec.push(take_fifth(new_vec)); // This pushes "None" i


option_vec.push(take_fifth(bigger_vec)); // This pushes "Some

handle_option(option_vec); // handle_option looks at every o


// It prints the value if it is S
}

fn take_fifth(value: Vec<i32>) -> Option<i32> {


if value.len() < 4 {
None
} else {
Some(value[4])
}
}

fn handle_option(my_option: Vec<Option<i32>>) {
for item in my_option {
match item {
Some(number) => println!("{}", number),
None => {}, // do nothing
}
}
}

Because we know generics, we can understand Option.


It looks like this:

enum Option<T> {
None,
Some(T),

81 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

The important point to remember: with Some , you have


a value of type T (any type). But with None , you don't
have anything. So in a match statement for Option you
can't say:

Some(value) => println!("The value is {}", value),


None(value) => println!("The value is {}", value),

because None is just None .

Of course, there are easier ways to use Option. In this


easier way, we don't need handle_option() anymore.
We also don't need a vec for the Options.

fn main() {
let new_vec = vec![1, 2];
let bigger_vec = vec![1, 2, 3, 4, 5];
let vec_of_vecs = vec![new_vec, bigger_vec];
for vec in vec_of_vecs {
let inside_number = take_fifth(vec);
if inside_number.is_some() { // .is_some() returns true
println!("{}", inside_number.unwrap

}
}
}

fn take_fifth(value: Vec<i32>) -> Option<i32> {


if value.len() < 4 {
None
} else {
Some(value[4])
}
}

Result

82 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Result is similar to Option, but here is the difference:

Option is about Some or None (value or no value),


Result is about Ok or Err (okay result, or error
result).

To compare, here are the signatures for Option and


Result.

enum Option<T> {
None,
Some(T),
}

enum Result<T, E> {


Ok(T),
Err(E),
}

So Result has a value inside of Ok , and a value inside


of Err . That is because errors usually have information
inside them.

Result<T, E> means you need to think of what you


want to return for Ok , and what you want to return for
Err . Actually, you can decide anything. Even this is
okay:

fn main() {
check_error();
}

fn check_error() -> Result<(), ()> {


Ok(())
}

check_error says "return () if we get Ok , and


return () if we get Err ". Then we return Ok with a
() .

83 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Sometimes a function with Result will use a String for


the Err value. This is not the best method to use, but
sometimes it is okay.

fn main() {
let mut result_vec = Vec::new(); // Create a new vec for the

for number in 2..7 {


result_vec.push(check_if_five(number));
}

println!("{:?}", result_vec);
}

fn check_if_five(number: i32) -> Result<i32, String


match number {
5 => Ok(number),
_ => Err("Sorry, the number wasn't five."
}
}

Our vec prints:

[Err("Sorry, the number wasn\'t five."),


Err("Sorry, the number wasn\'t five."),
Err("Sorry, the number wasn\'t five."),
Ok(5),
Err("Sorry, the number wasn\'t five.")]

Just like Option, .unwrap() on Err will panic.

fn main() {
let error_value: Result<i32, &str> = Err("There was an error
println!("{}", error_value.unwrap()); // Unwrap it
}

The program panics, and prints:

thread 'main' panicked at 'called

84 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

`Result::unwrap()` on an `Err` value: "There


was an error"', src\main.rs:30:20

This information helps you fix your code.


src\main.rs:30:20 means "inside main.rs in directory
src, on line 30 and column 20". So you can go there to
look at your code and fix the problem.

You can also create your own error types. Result


functions in the standard library usually have their own
error types. For example:

pub fn from_utf8(vec: Vec<u8>) -> Result<String

This function take a vector of bytes ( u8 ) and tries to


make a String . So the success case for the Result is
a String and the error case is FromUtf8Error . You
can give your error type any name you want. We will
create our own error types later, because first we need
to learn other things.

Using a match with Option and Result sometimes


requires a lot of code. For example, the .get()
method returns an Option on a Vec.

fn main() {
let my_vec = vec![2, 3, 4];
let get_one = my_vec.get(0); // 0 to get the first number
let get_two = my_vec.get(10); // Returns None
println!("{:?}", get_one);
println!("{:?}", get_two);
}

This prints

Some(2)

85 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

None

So now we can match to get the values. Let's use a


range from 0 to 10 to see if it matches the numbers in
my_vec .

fn main() {
let my_vec = vec![2, 3, 4];

for index in 0..10 {


match my_vec.get(index) {
Some(number) => println!("The number is: {}"
None => {}
}
}
}

This is good, but we don't do anything for None . Here


we can make the code smaller by using if let . if
let means "do something if it matches, and don't do
anything if it doesn't". if let is when you don't care
about matching for everything.

fn main() {
let my_vec = vec![2, 3, 4];

for index in 0..10 {


if let Some(number) = my_vec.get(index) {
println!("The number is: {}", number);
}
}
}

if let Some(number) = my_vec.get(index) means


"if you get Some(number) from my_vec.get(index) ".
Also note: it uses one = . It is not a boolean.

86 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

while let is like a while loop for if let . Imagine


that we have weather station data like this:

["Berlin", "cloudy", "5", "-7", "78"]


["Athens", "sunny", "not humid", "20", "10", "50"

We want to get the numbers, but not the words. For the
numbers, we can use a method called parse::
<i32>() . parse() is the method, and ::<i32> is the
type. It will try to turn the &str into an i32 , and give it
to us if it can.

We will also use .pop() . This takes the last item off of


the vector.

fn main() {
let mut weather_vec = vec!["Berlin", "cloudy"
while let Some(information) = weather_vec.pop

if let Ok(number) = information.parse::

println!("The number is: {}", number);


}
}
}

The ? operator
There is an even shorter way to deal with Result (and
Option), shorter than match and even shorter than if
let . It is called the "question mark operator", and is
just ? . After a function that returns a result, you can
add ? . This will:

return the result if it is Ok


pass the error back if it is Err

87 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

In other words, it does almost everything for you.

We can try this with .parse() again. We will write a


function called parse_str that tries to turn a &str
into a i32 . It looks like this:

fn parse_str(input: &str) -> Result<i32, std::num::ParseIntError


let parsed_number = input.parse::<i32>()?;
Ok(parsed_number)
}

This function takes a &str . If it is Ok , it gives an i32


wrapped in Ok . If it is an Err , it returns a
std::num::ParseIntError . Then we try to parse the
number, and add ? . That means "check if it is an error,
and give the result if it is okay". If it is not okay, it will
return the error and end. But if it is okay, it will go to the
next line. On the next line is the number inside of Ok() .
We need to wrap it in Ok because the return is
Result<i32, std::num::ParseIntError> , not i32 .

Now, we can try out our function. Let's see what it does
with a vec of &str s.

fn main() {
let str_vec = vec!["Seven", "8", "9.0", "nice"
for item in str_vec {
let parsed = parse_str(item);
println!("{:?}", parsed);
}
}

fn parse_str(input: &str) -> Result<i32, std::num::ParseIntError


let parsed_number = input.parse::<i32>()?;
Ok(parsed_number)
}

This prints:

88 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Err(ParseIntError { kind: InvalidDigit })


Ok(8)
Err(ParseIntError { kind: InvalidDigit })
Err(ParseIntError { kind: InvalidDigit })
Ok(6060)

How did we find std::num::ParseIntError ? One easy


way is to "ask" the compiler again.

fn main() {
let failure = "Not a number".parse::<i32>();
failure.rbrbrb(); // Compiler: "What is rbrbrb()???"
}

The compiler doesn't understand, and says:

error[E0599]: no method named `rbrbrb` found


for enum `std::result::Result<i32,
std::num::ParseIntError>` in the current
scope
--> src\main.rs:3:13
|
3 | failure.rbrbrb();
| ^^^^^^ method not found in
`std::result::Result<i32,
std::num::ParseIntError>`

So std::result::Result<i32,
std::num::ParseIntError> is the signature we need.

We don't need to write std::result::Result because


Result is always "in scope" (in scope = ready to use).
But std::num::ParseIntError is not in scope. We can
bring it in scope if we want:

use std::num::ParseIntError;

Then we can write:

89 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

use std::num::ParseIntError;

fn parse_str(input: &str) -> Result<i32, ParseIntError> {


let parsed_number = input.parse::<i32>()?;
Ok(parsed_number)
}

When panic and unwrap are good

Rust has a panic! macro that you can use to make it


panic. It is easy to use:

fn main() {
panic!("Time to panic!");
}

The message "Time to panic!" displays when you


run the program: thread 'main' panicked at 'Time
to panic!', src\main.rs:2:3

You will remember that src\main.rs is the directory


and file name, and 2:3 is the line and column name.
With this information, you can find the code and fix it.

panic! is a good macro to use when writing your code


to make sure that you know when something changes.
For example, our function prints_three_things
always prints index [0], [1], and [2] from a vector. It is
okay because we always give it a vector with three
items:

fn main() {
let my_vec = vec![8, 9, 10];
prints_three_things(my_vec);
}

90 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn prints_three_things(vector: Vec<i32>) {
println!("{}, {}, {}", vector[0], vector[1], vector[
}

But later on we write more and more code, and maybe


we forget that my_vec can only be three things. Now
my_vec in this part has six things:

fn main() {
let my_vec = vec![8, 9, 10, 10, 55, 99]; // Now my_vec has six
prints_three_things(my_vec);
}

fn prints_three_things(vector: Vec<i32>) {
println!("{}, {}, {}", vector[0], vector[1], vector[
}

No error happens, because [0] and [1] and [2] are all
inside. But maybe it was important to only have three
things. So we should have done this:

fn main() {
let my_vec = vec![8, 9, 10];
prints_three_things(my_vec);
}

fn prints_three_things(vector: Vec<i32>) {
if vector.len() != 3 {
panic!("my_vec must always have three items"
}
println!("{}, {}, {}", vector[0], vector[1], vector[
}

Now we will know if the vector has six items:

fn main() {
let my_vec = vec![8, 9, 10, 10, 55, 99];
prints_three_things(my_vec);

91 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn prints_three_things(vector: Vec<i32>) {
if vector.len() != 3 {
panic!("my_vec must always have three items"
}
println!("{}, {}, {}", vector[0], vector[1], vector[
}

This gives us thread 'main' panicked at 'my_vec


must always have three items', src\main.rs:8:9 .
Now we remember that my_vec should only have three
items. So panic! is a good macro to create reminders
in your code.

There are three other macros that are similar to


panic! that you use a lot in testing. They are:
assert! , assert_eq! , and assert_ne! .

They mean:

assert!() : if the part inside () is not true, the


program will panic. * assert_eq!() : the two items
inside () must be equal. * assert_ne!() : the two
items inside () must not be equal.

Some examples:

fn main() {
let my_name = "Loki Laufeyson";

assert!(my_name == "Loki Laufeyson");


assert_eq!(my_name, "Loki Laufeyson");
assert_ne!(my_name, "Mithridates");
}

This will do nothing, because all three assert macros


are okay.

You can also add a message if you want.

92 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn main() {
fn main() {
let my_name = "Loki Laufeyson";

assert!(
my_name == "Loki Laufeyson",
"{} should be Loki Laufeyson",
my_name
);
assert_eq!(
my_name, "Loki Laufeyson",
"{} and Loki Laufeyson should be equal"
my_name
);
assert_ne!(
my_name, "Mithridates",
"{} must not equal Mithridates",
my_name
);
}
}

These messages will only display if the program panics.


So if you run this:

fn main() {
let my_name = "Mithridates";

assert_ne!(
my_name, "Mithridates",
"{} must not equal Mithridates",
my_name
);
}

It will display:

thread 'main' panicked at 'assertion failed:


`(left != right)`

93 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

left: `"Mithridates"`,
right: `"Mithridates"`: Mithridates must
not equal Mithridates', src\main.rs:4:5

So it is saying "you said that left != right, but left ==


right". And it displays our message that says
Mithridates must not equal Mithridates .

unwrap is also good when you want the program to


crash when there is a problem. Later, when your code is
finished it is good to change unwrap to something else
that won't crash. You can also use expect , which is
like unwrap but with your own message.

This will crash:

fn main() {
let my_vec = vec![9, 0, 10];
let fourth = get_fourth(&my_vec);
}

fn get_fourth(input: &Vec<i32>) -> i32 {


let fourth = input.get(3).unwrap();
*fourth
}

The error message is thread 'main' panicked at


'called Option::unwrap() on a None value',
src\main.rs:7:18 .

Now we write our own message with expect :

fn main() {
let my_vec = vec![9, 0, 10];
let fourth = get_fourth(&my_vec);
}

fn get_fourth(input: &Vec<i32>) -> i32 {


let fourth = input.get(3).expect("Input vector needs at leas

94 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

*fourth
}

So the error message is thread 'main' panicked at


'Input vector needs at least 4 items',
src\main.rs:7:18 .

You can also use unwrap_or if you want to always


have a value that you choose.

fn main() {
let my_vec = vec![8, 9, 10];

let fourth = my_vec.get(3).unwrap_or(&0); // If .get doesn't


// .get returns a
// to keep the typ
// You can write "
// a 0 and not a &

println!("{}", fourth);
}

Traits
We have seen traits before: Debug, Copy, Clone are all
traits. To give a type a trait, you have to implement it.
Because Debug and the others are so common, it's
easy to do:

#[derive(Debug)]
struct MyStruct {
number: usize,
}

95 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

But other traits are more difficult, so you need to


implement them manually with impl . For example,
std::ops::Add is used to add two things. But you can
add in many ways.

struct ThingsToAdd {
first_thing: u32,
second_thing: f32,
}

We can add first_thing and second_thing , but we


need to give more information. Maybe we want an f32 ,
so something like this:

let result = self.second_thing + self.first_thing

But maybe we want an integer, so like this:

let result = self.second_thing as u32 + self.first_thing

Or maybe we want to just put self.first_thing next to


self.second_thing and say that this is how we want
to add. So if we add 55 to 33.4, we want to see 5533,
not 88.

So first let's look at how to make a trait. To make a trait,


write trait and then create some functions.

struct Animal { // A simple struct - an Animal only has a name


name: String,
}

trait Dog { // The dog trait gives some functionality


fn bark(&self) { // It can bark
println!("Woof woof!");
}

96 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn run(&self) { // and it can run


println!("The dog is running!");
}
}

impl Dog for Animal {} // Now Animal has the trait Dog

fn main() {
let rover = Animal {
name: "Rover".to_string(),
};

rover.bark(); // Now Animal can use bark()


rover.run(); // and it can use run()
}

This is okay, but we don't want to print "The dog is


running". We can change the method .run() , but we
have to follow the signature. The signature says:

fn run(&self) {
println!("The dog is running!");
}

The signature says "fn run() takes &self , and


returns nothing". So you can't do this:

fn run(&self) -> i32 {


5
}

Rust will say:

= note: expected fn pointer `fn(&Animal)`


found fn pointer `fn(&Animal)
-> i32`

But we can do this:

97 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

impl Dog for Animal {


fn run(&self) {
println!("{} is running!", self.name);
}
}

Now it prints Rover is running! . This is okay


because we are returning () , or nothing, which is what
the trait says.

Actually, a trait doesn't need to write out the whole


function. Now we change bark() and run() to just
say fn bark(&self) and fn run(&self); . This is not
a full function, so the user must write it.

struct Animal {
name: String,
}

trait Dog {
fn bark(&self); // bark() says it needs a &self and returns
fn run(&self); // run() says it needs a &self and returns no
// So now we have to write them ourselves.
}

impl Dog for Animal {


fn bark(&self) {
println!("{}, stop barking!!", self.name);
}
fn run(&self) {
println!("{} is running!", self.name);
}
}

fn main() {
let rover = Animal {
name: "Rover".to_string(),
};

rover.bark();

98 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

rover.run();
}

So when you create a trait, you must think: "Which


functions should I write? And which functions should the
user write?" If you think the user will use the function the
same way every time, then write out the function. If you
think the user will use it differently, then just write the
function signature.

So let's try implementing the Display trait for our struct.


First we will make a simple struct:

struct Cat {
name: String,
age: u8,
}

fn main() {
let mr_mantle = Cat {
name: "Reggie Mantle".to_string(),
age: 4,
};
}

Now we want to print mr_mantle . Debug is easy to


derive:

#[derive(Debug)]
struct Cat {
name: String,
age: u8,
}

fn main() {
let mr_mantle = Cat {
name: "Reggie Mantle".to_string(),
age: 4,
};

99 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

println!("Mr. Mantle is a {:?}", mr_mantle);


}

but Debug print is not what we want.

Mr. Mantle is a Cat { name: "Reggie Mantle", age:

So we need to implement Display for Cat. On


https://doc.rust-lang.org/std/fmt/trait.Display.html we can
see the information for Display, and one example. It
says:

use std::fmt;

struct Position {
longitude: f32,
latitude: f32,
}

impl fmt::Display for Position {


fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::
write!(f, "({}, {})", self.longitude, self
}
}

Some parts of this we don't understand yet, like <'_>


and what f is doing. But we understand the Position
struct: it is just two f32 s. We also understand that
self.longitude and self.latitude are the values
in the struct. So maybe we can just use this for our
struct, with self.name and self.age . Also, write!
looks a lot like println! . So we write this:

use std::fmt;

struct Cat {

100 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

name: String,
age: u8,
}

impl fmt::Display for Cat {


fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::
write!(f, "{} is a cat who is {} years old."
}
}

Now our code looks like this:

use std::fmt;
struct Cat {
name: String,
age: u8,
}

impl fmt::Display for Cat {


fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::
write!(f, "{} is a cat who is {} years old."
}
}

fn main() {
let mr_mantle = Cat {
name: "Reggie Mantle".to_string(),
age: 4,
};

println!("{}", mr_mantle);
}

Success! Now when we use {} to print, we get


Reggie Mantle is a cat who is 4 years old. . This
looks much better.

Taking a String and a &str in a


function

101 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Sometimes you want a function that can take both a


String and a &str . You can do this with generics
and the AsRef trait. AsRef is used to give a reference
from one type to another type. If you look at the
documentation for String , you can see that it has
AsRef for many types:

https://doc.rust-lang.org/std/string/struct.String.html

Here are some function signatures for them.

AsRef<str> :

impl AsRef<str> for String

fn as_ref(&self) -> &str

AsRef<[u8]> :

impl AsRef<[u8]> for String

fn as_ref(&self) -> &[u8]

AsRef<OsStr> :

impl AsRef<OsStr> for String

fn as_ref(&self) -> &OsStr

You can see that it takes &self and gives a reference


to the other type. This means that if you have a generic
type T, you can say that it needs AsRef<str> . If you do
that, it will be able to take a &str and a String .

Let's start with the generic function. This doesn't work


yet:

102 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn print_it<T>(input: T) {
println!("{}", input)
}

fn main() {
print_it("Please print me");
}

Rust says error[E0277]: T doesn't implement


std::fmt::Display . So we will require T to implement
Display.

use std::fmt::Display;

fn print_it<T: Display>(input: T) {
println!("{}", input)
}

fn main() {
print_it("Please print me");
}

Now it works and prints Please print me . That is


good, but T can still be too many things. It can be an
i8 , an f32 and anything else with just Display . So
we add AsRef<str> , and now T needs both
AsRef<str> and Display .

use std::fmt::Display;

fn print_it<T: AsRef<str> + Display>(input: T) {


println!("{}", input)
}

fn main() {
print_it("Please print me");
print_it("Also, please print me".to_string());
// print_it(7); <- This will not print

103 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Now it won't take types like i8 .

Don't forget that you can write the function differently


when it gets long. If we add Debug then it becomes fn
print_it<T: AsRef<str> + Display + Debug>
(input: T) which is long for one line. So we can write
it like this:

use std::fmt::{Debug, Display}; // add Debug

fn print_it<T>(input: T) // Now this line is easy to read


where
T: AsRef<str> + Debug + Display, // and these traits are eas
{
println!("{}", input)
}

fn main() {
print_it("Please print me");
print_it("Also, please print me".to_string());
}

Chaining methods
Rust is a systems programming language, but it also
has a functional style. Both styles are okay, but
functional style is usually shorter. Here is an example of
declarative style to make a Vec from 1 to 10:

fn main() {
let mut new_vec = Vec::new();
let mut counter = 1;

while counter < 11 {


new_vec.push(counter);

104 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

counter += 1;
}

println!("{:?}", new_vec);
}

And here is an example of functional style:

fn main() {
let new_vec = (1..=10).collect::<Vec<i32>>();
// Or you can write it like this:
// let new_vec: Vec<i32> = (1..=10).collect();
println!("{:?}", new_vec);
}

.collect() can make collections of many types, so


we have to tell it the type.

With functional style you can chain methods. That


means to put many methods together in a single
statement. Here is an example of many methods
chained together:

fn main() {
let my_vec = vec![0, 1, 2, 3, 4, 5, 6, 7, 8

let new_vec = my_vec.into_iter().skip(3).take

println!("{:?}", new_vec);
}

This creates a Vec with [3, 4, 5, 6] . It is a good


idea to put each method on a new line if you have many
chained methods. This helps you to read the code. Here
is the same code with each method on a new line:

fn main() {

105 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

let my_vec = vec![0, 1, 2, 3, 4, 5, 6, 7, 8

let new_vec = my_vec


.into_iter() // "iterate" over the items (iterate = work
.skip(3) // skip over three items: 0, 1, and 2
.take(4) // take the next four: 3, 4, 5, and 6
.collect::<Vec<i32>>(); // put them in a new Vec<i32>

println!("{:?}", new_vec);
}

You can use this functional style best when you


understand closures and iterators. So we will learn them
next.

Iterators
An iterator is a collection that can give you the items in
the collection, one at a time. Actually, we have already
used iterators: the for loop gives you an iterator.
When you want to use an iterator other times, you have
to choose what kind:

.iter() for an iterator of references

.iter_mut() for an iterator of mutable references

.into_iter() for an iterator of values (not


references)

We can use them like this:

fn main() {
let vector1 = vec![1, 2, 3]; // for .iter() and .into_iter()
let mut vector2 = vec![10, 20, 30]; // for .iter_mut()

let vector1_a = vector1.iter().map(|x| x +


vector2.iter_mut().for_each(|x| *x +=100);
let vector1_b = vector1.into_iter().map(|x|

106 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

println!("{:?}", vector1_a);
println!("{:?}", vector2);
println!("{:?}", vector1_b);

First we used .iter() on vector1 to get references.


We added 1 to each, and made it into a new Vec.
vector1 is still alive because we only used references:
we didn't take by value. Now we have vector1 , and a
new Vec called vector1_a .

Then we used .iter_mut() for vector2 . It is


mutable, so we don't need to use .collect() to create
a new Vec. Instead, we change the values in the same
Vec with mutable references. So vector2 is still there.
Because we don't need a new Vec, we use for_each :
it's just like a for loop.

Finally we used into_iter to get an iterator by value


from vector1 . This destroys vector1 , so after we
make vector1_b we can't use vector1 again.

How does an iterator work?

An iterator works by using a method called .next() ,


which gives an Option . When you use an iterator, Rust
calls next() . If it gets Some , it keeps going. If it gets
None , it stops.

In documentation, you see examples like this to show


how an iterator works.

fn main() {
let my_vec = vec!['a', 'b', ' 거 ', '柳'];

let mut my_vec_iter = my_vec.iter(); // This is an iterator

107 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

assert_eq!(my_vec_iter.next(), Some(&'a'));
assert_eq!(my_vec_iter.next(),
assert_eq!(my_vec_iter.next(),
assert_eq!(my_vec_iter.next(),
Some(&' 거
Some(&'b'));
'));
Some(&'柳'));
assert_eq!(my_vec_iter.next(), None);
assert_eq!(my_vec_iter.next(), None); // You can keep callin
}

Implementing Iterator for your own struct or enum is not


too hard. First let's make a book library and think about
it.

#[derive(Debug)] // we want to print it with {:?}


struct Library {
library_type: LibraryType, // this is our enum
books: Vec<String>, // list of books
}

#[derive(Debug)]
enum LibraryType { // libraries can be city libraries or country
City,
Country,
}

impl Library {
fn add_book(&mut self, book: &str) { // we use add_book to a
self.books.push(book.to_string()); // we take a &str and
}

fn new() -> Self { // this creates a new Library


Self {
library_type: LibraryType::City, // most are in the
// most of the time
books: Vec::new(),
}
}
}

fn main() {
let mut my_library = Library::new(); // make a new library
my_library.add_book("The Doom of the Darksword"

108 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

my_library.add_book(" 구운몽
my_library.add_book("Demian - die Geschichte einer Jugend"
");
my_library.add_book("吾輩は猫である");

println!("{:?}", my_library.books); // we can print our list


}

That works well. Now we want to implement Iterator


for the library so we can use it in a for loop. Right now
if we try a for loop, it doesn't work:

for item in my_library {


println!("{}", item);
}

It says:

error[E0277]: `Library` is not an iterator


--> src\main.rs:47:16
|
47 | for item in my_library {
| ^^^^^^^^^^ `Library` is
not an iterator
|
= help: the trait `std::iter::Iterator`
is not implemented for `Library`
= note: required by
`std::iter::IntoIterator::into_iter`

But we can make library into an iterator with impl


Iterator for Library . Information on the Iterator
trait is here in the standard library: https://doc.rust-
lang.org/std/iter/trait.Iterator.html

109 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

On the top left of the page it says: Associated Types:


Item and Required Methods: next . An "associated
type" means "a type that goes together". Our associated
type will be String , because we want the iterator to
give us Strings.

In the page it has an example that looks like this:

// an iterator which alternates between Some and None


struct Alternate {
state: i32,
}

impl Iterator for Alternate {


type Item = i32;

fn next(&mut self) -> Option<i32> {


let val = self.state;
self.state = self.state + 1;

// if it's even, Some(i32), else None


if val % 2 == 0 {
Some(val)
} else {
None
}
}
}

You can see that under impl Iterator for


Alternate it says type Item = i32 . This is the
associated type. Our iterator will be for our list of books,
which is a Vec<String>> . When we call next, it will
give us a String . So we will write type Item =
String; . That is the associated item.

110 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

To implement Iterator , you need to write the fn


next() function. This is where you decide what the
iterator should do. For our Library , we want it to give
us the last books first. So we will match with .pop()
which takes the last item off it it is Some . We also want
to print " is found!" for each item. Now it looks like this:

#[derive(Debug)]
struct Library {
library_type: LibraryType,
books: Vec<String>,
}

#[derive(Debug)]
enum LibraryType {
City,
Country,
}

impl Library {
fn add_book(&mut self, book: &str) {
self.books.push(book.to_string());
}

fn new() -> Self {


Self {
library_type: LibraryType::City,
// most of the time
books: Vec::new(),
}
}
}

impl Iterator for Library {


type Item = String;

fn next(&mut self) -> Option<String> {


match self.books.pop() {
Some(book) => Some(book + " is found!"
None => None,
}

111 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

}
}

fn main() {
let mut my_library = Library::new();
my_library.add_book("The Doom of the Darksword"

my_library.add_book(" 구운몽
my_library.add_book("Demian - die Geschichte einer Jugend"
");
my_library.add_book("吾輩は猫である");

for item in my_library { // we can use a for loop now


println!("{}", item);
}
}

This prints:

구운몽
吾輩は猫である is found!
is found!
Demian - die Geschichte einer Jugend is
found!
The Doom of the Darksword is found!

Closures
Closures are like quick functions that don't need a
name. Sometimes they are called lambdas. Closures
are easy to find because they use || instead of () .

You can bind a closure to a variable, and then it looks


like a function:

fn main() {
let my_closure = || println!("This is a closure"
my_closure();
}

112 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

So this closure takes nothing: || and prints a


message.

In between the || we can add input variables and


types:

fn main() {
let my_closure = |x: i32| println!("{}", x);

my_closure(5);
}

When the closure becomes more complicated, you can


add a code block. Then it can be as long as you want.

fn main() {
let my_closure = || {
let number = 7;
let other_number = 10;
println!("The two numbers are {} and {}."
// This closure can be as long as we want, just like a
};

my_closure();
}

But closures are special because they can take


variables outside the closure. So you can do this:

fn main() {
let number_one = 6;
let number_two = 10;

let my_closure = || println!("{}", number_one


my_closure();
}

113 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

So this prints 16 . You didn't need to put anything


inside || because the closure can just take them.

And you can do this:

fn main() {
let number_one = 6;
let number_two = 10;

let my_closure = |x: i32| println!("{}", number_one


my_closure(5);
}

This closure takes number_one and number_two . We


also gave it a new variable x and said that x is 5.
Then it adds all three together.

Usually you see closures in Rust inside of a method,


because it is very convenient to have a closure inside.
For example, there is the unwrap_or method that we
know that you can use to give a value if unwrap
doesn't work. Before, we wrote: let fourth =
my_vec.get(3).unwrap_or_(&0); . But there is also an
unwrap_or_else method that has a closure inside. So
you can do this:

fn main() {
let my_vec = vec![8, 9, 10];

let fourth = my_vec.get(3).unwrap_or_else(||


if my_vec.get(0).is_some() { // see if my_vec has someth
&my_vec[0] // Give the number at index 0 if there is
} else {
&0 // otherwise give a &0
}
});

println!("{}", fourth);
}

114 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Of course, a closure can be very simple. You can just


write let fourth =
my_vec.get(3).unwrap_or_else(|| &0); for example.
As long as you put the || in, the compiler knows that
you have put in the closure that you need.

The most frequent closure method is maybe .map() .


This method does something to each item. Here is one
way to use it:

fn main() {
let num_vec = vec![2, 4, 6];

let double_vec = num_vec // take num_vec


.iter() // iterate over it
.map(|number| number * 2) // for each item, multiply by
.collect::<Vec<i32>>(); // then make a new Vec from this
println!("{:?}", double_vec);
}

Another good example is with .enumerate() . This


gives an iterator with the index number, and the item.
For example: [10, 9, 8] becomes (0, 10), (1,
9), (2, 8) . So you can do this:

fn main() {
let num_vec = vec![10, 9, 8];

num_vec
.iter() // iterate over num_vec
.enumerate() // get (index, number)
.for_each(|(index, number)| println!("Index number {} ha
}

This prints:

Index number 0 has number 10


Index number 1 has number 9

115 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Index number 2 has number 8

In this case we use for_each instead of map . map is


for doing something to each item and passing it on,
and for_each is doing something when you see
each item. Also, map doesn't do anything unless you
use a method like collect .

Actually, this is the interesting thing about iterators. If


you try to map without a method like collect , the
compiler will tell you that it doesn't do anything:

fn main() {
let num_vec = vec![10, 9, 8];

num_vec
.iter()
.enumerate()
.map(|(index, number)| println!("Index number {} has num

It says:

warning: unused `std::iter::Map` that must


be used
--> src\main.rs:4:5
|
4 | / num_vec
5 | | .iter()
6 | | .enumerate()
7 | | .map(|(index, number)|
println!("Index number {} has number {}",
index, number));
|
|_______________________________________________________________
|
= note: `#[warn(unused_must_use)]` on by
default

116 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

= note: iterators are lazy and do nothing


unless consumed

This is a warning, so it's not an error: the program runs


fine. But why doesn't num_vec do anything? We can
look at the types to see.

let num_vec = vec![10, 9, 8]; Right now it is a


Vec<i32> .

.iter() Now it is an Iter<i32> . So it is an


iterator with items of i32 .
.enumerate() Now it is an
Enumerate<Iter<i32>> . So it is a type
Enumerate of type Item of i32 s.

.map() Now it is a type


Map<Enumerate<Iter<i32>>> . So it is a type Map
of type Enumerate of type Item of i32 s.

So this Map<Enumerate<Iter<i32>>> is a structure that


is ready to go, when we tell it what to do. Rust does this
because it needs to be fast. It doesn't want to do this:

iterate over all the i32 s in the Vec


enumerate over all the i32 s from the iterator
map over all the enumerated i32 s

Rust only wants to do one calculation, so it creates the


structure and waits. Then if we say .collect::
<Vec<i32>>() it knows what to do, and starts moving.
This is what iterators are lazy and do nothing
unless consumed means. The iterators don't do
anything until you "consume" them (use them up).

|_| in a closure

117 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Sometimes you see |_| in a closure. This means that


the closure needs an argument, but you don't need to
use it. So |_| means "Okay, here is the argument but I
won't give it a name because I don't care about it".

Here is an example of an error:

fn main() {
let my_vec = vec![8, 9, 10];

println!("{:?}", my_vec.iter().for_each(||
}

Rust says that

error[E0593]: closure is expected to take 1


argument, but it takes 0 arguments
--> src\main.rs:28:36
|
28 | println!("{:?}",
my_vec.iter().for_each(|| println!("We
didn't use the variables at all")));
|
^^^^^^^^ -- takes 0 arguments
| |
|
expected closure that takes 1 argument

The compiler actually gives you some help:

help: consider changing the closure to take


and ignore the expected argument
|
28 | println!("{:?}",
my_vec.iter().for_each(|_| println!("We
didn't use the variables at all")));

This is good advice. If you change || to |_| then it


will work.

118 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

The dbg! macro and .inspect

dbg! is a very useful macro that prints quick


information. Sometimes you use it instead of println!
because it is faster to type:

fn main() {
let my_number = 8;
dbg!(my_number);
}

This prints [src\main.rs:4] my_number = 8 .

But actually, you can put dbg! in many other places.


Look at this code for example:

fn main() {
let mut my_number = 9;
my_number += 10;

let new_vec = vec![8, 9, 10];

let double_vec = new_vec.iter().map(|x| x *


}

This code creates a new mutable number and changes


it. Then it creates a vec, and uses iter and map and
collect to create a new vec. We can put dbg!
almost everywhere in this code. dbg! asks the
compiler: "What are you doing here?"

fn main() {
let mut my_number = dbg!(9);
dbg!(my_number += 10);

let new_vec = dbg!(vec![8, 9, 10]);

119 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

let double_vec = dbg!(new_vec.iter().map(|x

dbg!(double_vec);
}

So this prints:

[src\main.rs:3] 9 = 9

and:

[src\main.rs:4] my_number += 10 = ()

and:

[src\main.rs:6] vec![8, 9, 10] = [


8,
9,
10,
]

and:

[src\main.rs:8] new_vec.iter().map(|x| x *
2).collect::<Vec<i32>>() = [
16,
18,
20,
]

and:

[src\main.rs:10] double_vec = [
16,
18,
20,

120 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

.inspect is a bit similar to dbg! but you use it like


map . For example, let's look at our double_vec again.

fn main() {
let new_vec = vec![8, 9, 10];

let double_vec = new_vec


.iter()
.map(|x| x * 2)
.collect::<Vec<i32>>();
}

We want to know more information about what the code


is doing. So we add inspect in two places:

fn main() {
let new_vec = vec![8, 9, 10];

let double_vec = new_vec


.iter()
.inspect(|first_item| println!("The item is: {}"
.map(|x| x * 2)
.inspect(|next_item| println!("Then it is: {}"
.collect::<Vec<i32>>();
}

This prints:

The item is: 8


Then it is: 16
The item is: 9
Then it is: 18
The item is: 10
Then it is: 20

121 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

And because .inspect takes a closure, we can write


as much as we want:

fn main() {
let new_vec = vec![8, 9, 10];

let double_vec = new_vec


.iter()
.inspect(|first_item| {
println!("The item is: {}", first_item);
match **first_item % 2 { // first item is a &&i32 so w
0 => println!("It is even."),
_ => println!("It is odd."),
}
println!("In binary it is {:b}.", first_item);
})
.map(|x| x * 2)
.collect::<Vec<i32>>();
}

This prints:

The item is: 8


It is even.
In binary it is 1000.
The item is: 9
It is odd.
In binary it is 1001.
The item is: 10
It is even.
In binary it is 1010.

Types of &str
There is more than one type of &str . We have:

String literals: you make these when you write let


my_str = "I am a &str" . They last for the whole
program, because they are written directly into the

122 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

binary. They have the type &'static str . '


means its lifetime, and string literal have a lifetime
called static .
Borrowed str: This is the regular &str form without
a static lifetime. If you create a String and get
a reference to it, Rust will convert it to a &str
when you need it. For example:

fn main() {
let my_string = String::from("I am a string"
prints_str(&my_string); // we give prints_str a &String
}

fn prints_str(my_str: &str) { // it can use &String like a &str


println!("{}", my_str);
}

So what is a lifetime?

Lifetimes
A lifetime means "how long the variable lives". You only
need to think about lifetimes with references. This is
because references can't live longer than the object
they come from. For example, this function does not
work:

fn returns_reference() -> &str {


let my_string = String::from("I am a string"
&my_string
}

The problem is that my_string only lives inside


returns_reference . We try to return &my_string , but
&my_string can't exist without my_string . So the
compiler says no.

123 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

This code also doesn't work:

fn main() {
let my_str = returns_str();
println!("{}", my_str);
}

fn returns_str() -> &str {


let my_string = String::from("I am a string"
"I am a str"
}

But it almost works. The compiler says:

error[E0106]: missing lifetime specifier


--> src\main.rs:6:21
|
6 | fn returns_str() -> &str {
| ^ expected named
lifetime parameter
|
= help: this function's return type
contains a borrowed value, but there is no
value for it to be borrowed from
help: consider using the `'static` lifetime
|
6 | fn returns_str() -> &'static str {
| ^^^^^^^^

missing lifetime specifier means that we need to


add a ' with the lifetime. Then it says that it contains
a borrowed value, but there is no value for it
to be borrowed from . That means that I am a str
isn't borrowed from anything. It says consider using
the 'static lifetime by writing &'static str . So it
thinks we should try saying that this is a string literal.

Now it works:

124 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn main() {
let my_str = returns_str();
println!("{}", my_str);
}

fn returns_str() -> &'static str {


let my_string = String::from("I am a string"
"I am a str"
}

So now fn returns_str() -> &'static str tells


Rust: "don't worry, we will only return a string literal".
String literals live for the whole program, so Rust is
happy.

But 'static is not the only lifetime. Actually, every


variable has a lifetime, but usually we don't have to write
it. We only have to write the lifetime when the compiler
doesn't know.

Here is an example of another lifetime. Imagine we want


to create a City struct and give it a &str for the
name. Later we will have many more &str s because
we need faster performance than with String . So we
write it like this, but it won't work:

#[derive(Debug)]
struct City {
name: &str,
date_founded: u32,
}

fn main() {
let my_city = City {
name: "Ichinomiya",
date_founded: 1921,
};
}

125 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

The compiler says:

error[E0106]: missing lifetime specifier


--> src\main.rs:3:11
|
3 | name: &str,
| ^ expected named lifetime
parameter
|
help: consider introducing a named lifetime
parameter
|
2 | struct City<'a> {
3 | name: &'a str,
|

Rust needs a lifetime for &str because &str is a


reference. What happens when the value that name
points to is dropped? That would be unsafe.

What about 'static , will that work? We used it before.


Let's try:

#[derive(Debug)]
struct City {
name: &'static str, // change &str to &'static str
date_founded: u32,
}

fn main() {
let my_city = City {
name: "Ichinomiya",
date_founded: 1921,
};

println!("{} was founded in {}", my_city.name, my_city.date_


}

126 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Okay, that works. And maybe this is what you wanted


for the struct. However, note that we can only take
"string literals", so not references to something else. So
this will not work:

#[derive(Debug)]
struct City {
name: &'static str, // must live for the whole program
date_founded: u32,
}

fn main() {
let city_names = vec!["Ichinomiya".to_string

let my_city = City {


name: &city_names[0], // This is a &str, but not a &'sta
date_founded: 1921,
};

println!("{} was founded in {}", my_city.name, my_city.date_


}

The compiler says:

error[E0597]: `city_names` does not live


long enough
--> src\main.rs:12:16
|
12 | name: &city_names[0],
| ^^^^^^^^^^
| |
| borrowed value does not
live long enough
| requires that
`city_names` is borrowed for `'static`
...
18 | }
| - `city_names` dropped here while still
borrowed

127 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

So now we will try what the compiler suggested before.


It said to try writing struct City<'a> and name: &'a
str . This means that it will only take a reference for
name if it lives as long as City .

#[derive(Debug)]
struct City<'a> { // City has lifetime 'a
name: &'a str, // and name also has lifetime 'a.
date_founded: u32,
}

fn main() {
let city_names = vec!["Ichinomiya".to_string

let my_city = City {


name: &city_names[0],
date_founded: 1921,
};

println!("{} was founded in {}", my_city.name, my_city.date_


}

Also remember that you can write anything instead of


'a if you want:

#[derive(Debug)]
struct City<'city> { // The lifetime is now called 'city
name: &'city str, // and name has the 'city lifetime
date_founded: u32,
}

So usually you will write 'a, 'b, 'c etc. because it is


quick and the usual way to write. But you can always
change it if you want.

Also remember this important fact: 'a etc. don't


change the actual lifetime of variables. They are like
traits for generics. Remember when we wrote generics?
For example:

128 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn prints<T: Display>(input: T) {
println!("T is {}", input);
}

When you write T: Display , it means "please only


take T if it has Display". It does not mean: "I am giving
Display to T".

The same is true for lifetimes. When you write 'a here:

#[derive(Debug)]
struct City<'a> {
name: &'a str,
date_founded: u32,
}

It means "please only take an input for name if it lives


at least as long as City ". It does not mean: "I will
make the input for name live as long as City ".

Interior mutability

Cell

Interior mutability means having a little bit of mutability


on the inside. Rust has some ways to let you safely
change values inside of a struct that is immutable. First,
let's look at a simple example where we would want
this. Imagine a struct called PhoneModel with many
fields:

struct PhoneModel {
company_name: String,
model_name: String,
screen_size: f32,

129 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

memory: usize,
date_issued: u32,
on_sale: bool,
}

fn main() {
let super_phone_3000 = PhoneModel {
company_name: "YY Electronics".to_string
model_name: "Super Phone 3000".to_string
screen_size: 7.5,
memory: 4_000_000,
date_issued: 2020,
on_sale: true,
};

It is better for the fields in PhoneModel to be


immutable, because we don't want the data to change.
The date_issued and screen_size never change, for
example.

But inside is one field called on_sale . A phone model


will first be on sale ( true ), but later the company will
stop selling it. Can we make just this one field mutable?
Because we don't want to write let mut
super_phone_3000 . If we do, then every field will
become mutable.

Rust has many ways to allow some safe mutability


inside of something that is immutable. The most simple
is called Cell . First we use use std::cell::Cell so
that we can just write Cell instead of
std::cell::Cell every time.

Then we change on_sale: bool to on_sale:


Cell<bool> . Now it is a bool inside of the Cell .

130 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Cell has a method called .set() where you can


change the value. We use .set() to change
on_sale: true to on_sale: Cell::new(true) .

use std::cell::Cell;

struct PhoneModel {
company_name: String,
model_name: String,
screen_size: f32,
memory: usize,
date_issued: u32,
on_sale: Cell<bool>,
}

fn main() {
let super_phone_3000 = PhoneModel {
company_name: "YY Electronics".to_string
model_name: "Super Phone 3000".to_string
screen_size: 7.5,
memory: 4_000_000,
date_issued: 2020,
on_sale: Cell::new(true),
};

// 10 years later, super_phone_3000 is not on sale anymore


super_phone_3000.on_sale.set(false);
}

Cell works for all types, but works best for simple
Copy types because it gives values, not references.
Cell has a method called get() for example that
only works on Copy types.

Another type you can use is RefCell .

RefCell

131 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

A RefCell is another way to change values without


needing to declare mut . It is like a Cell but uses
references instead of copies.

We will create a User struct. So far you can see that it


is similar to Cell :

use std::cell::RefCell;

#[derive(Debug)]
struct User {
id: u32,
year_registered: u32,
username: String,
active: RefCell<bool>,
// Many other fields
}

fn main() {
let user_1 = User {
id: 1,
year_registered: 2020,
username: "User 1".to_string(),
active: RefCell::new(true),
};

println!("{:?}", user_1.active);
}

This prints RefCell { value: true } .

There are many methods for RefCell . Two of them are


.borrow() and .borrow_mut() . With these methods,
you can do the same thing you do with & and &mut .
The rules are the same:

Many borrows is fine,


one mutable borrow is fine,
but mutable and immutable together is not fine.

132 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

So changing the value in a RefCell is very easy:

user_1.active.replace(false);
println!("{:?}", user_1.active);

And there are many other methods like replace_with


that uses a closure:

let date = 2020;

user_1
.active
.replace_with(|_| if date < 2000 { true
println!("{:?}", user_1.active);

But you have to be careful with a RefCell , because it


checks borrows at runtime, not compilation time. So this
will compile:

use std::cell::RefCell;

#[derive(Debug)]
struct User {
id: u32,
year_registered: u32,
username: String,
active: RefCell<bool>,
// Many other fields
}

fn main() {
let user_1 = User {
id: 1,
year_registered: 2020,
username: "User 1".to_string(),
active: RefCell::new(true),
};

let borrow_one = user_1.active.borrow_mut();

133 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

let borrow_two = user_1.active.borrow_mut();


}

But if you run it, it will immediately panic.

thread 'main' panicked at 'already borrowed:


BorrowMutError', C:\Users\mithr\.rustup
\toolchains\stable-x86_64-pc-windows-
msvc\lib/rustlib/src/rust\src\libcore
\cell.rs:877:9
note: run with `RUST_BACKTRACE=1`
environment variable to display a backtrace
error: process didn't exit successfully:
`target\debug\rust_book.exe` (exit code:
101)

already borrowed: BorrowMutError is the important


part. So when you use a RefCell , it is good to compile
and run to check.

Mutex
Mutex is another way to change values without
declaring mut . Mutex means mutual exclusion ,
which means "only one at a time". This is why a Mutex
is safe, because it only lets one process change it at a
time. To do this, it uses .lock() . Lock is like locking
a door from the inside. You go into a room, lock the
door, and now you can change things inside the room.
Nobody else can come in and stop you, because you
locked the door.

A Mutex is easier to understand through examples.

use std::sync::Mutex;

134 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn main() {
let my_mutex = Mutex::new(5); // A new Mutex<i32>. We don't
let mut mutex_changer = my_mutex.lock().unwrap

println!("{:?}", my_mutex); // This prints "Mutex { data: <l


// So we can't access the data w
// only with mutex_changer

println!("{:?}", mutex_changer); // This prints 5. Let's cha

*mutex_changer = 6; // mutex_changer is a MutexGuard<i32> so

println!("{:?}", mutex_changer); // Now it says 6


}

But mutex_changer still has a lock. How do we stop it?


A Mutex is unlocked when the MutexGuard goes out
of scope. "Go out of scope" means the code block is
finished. For example:

use std::sync::Mutex;

fn main() {
let my_mutex = Mutex::new(5);
{
let mut mutex_changer = my_mutex.lock().
*mutex_changer = 6;
} // mutex_changer goes out of scope - now it is gone

println!("{:?}", my_mutex); // Now it says 6


}

If you don't want to use a different {} code block, you


can use std::mem::drop(mutex_changer) .
std::mem::drop means "make this go out of scope".

use std::sync::Mutex;

135 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn main() {
let my_mutex = Mutex::new(5);
let mut mutex_changer = my_mutex.lock().unwrap
*mutex_changer = 6;
std::mem::drop(mutex_changer); // drop mutex_changer - it is
// and my_mutex is unlocked

println!("{:?}", my_mutex); // Now it says 6


}

You have to be careful with a Mutex because if another


variable tries to lock it, it will wait:

use std::sync::Mutex;

fn main() {
let my_mutex = Mutex::new(5);
let mut mutex_changer = my_mutex.lock().unwrap
let mut other_mutex_changer = my_mutex.lock

println!("This will never print...");


}

One other method is try_lock() . Then it will try once,


and if it doesn't get the lock it will give up. Don't do
try_lock().unwrap() , because it will panic if it
doesn't work. if let or match is better:

use std::sync::Mutex;

fn main() {
let my_mutex = Mutex::new(5);
let mut mutex_changer = my_mutex.lock().unwrap
let mut other_mutex_changer = my_mutex.try_lock

if let Ok(value) = other_mutex_changer {

136 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

println!("The MutexGuard has: {}", value)


} else {
println!("Didn't get the lock")
}
}

Also, you don't need to make a variable to change the


Mutex . You can just do this:

use std::sync::Mutex;

fn main() {
let my_mutex = Mutex::new(5);

*my_mutex.lock().unwrap() = 6;

println!("{:?}", my_mutex);
}

*my_mutex.lock().unwrap() = 6; means "unlock


my_mutex and make it 6". There is no variable that
holds it so you don't need to call std::mem::drop . You
can do it 100 times if you want - it doesn't matter:

use std::sync::Mutex;

fn main() {
let my_mutex = Mutex::new(5);

for _ in 0..100 {
*my_mutex.lock().unwrap() += 1; // locks and unlocks 100
}

println!("{:?}", my_mutex);
}

RwLock

137 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

RwLock means "read write lock". It is like a Mutex but


also like a RefCell . You use .write().unwrap()
instead of .lock().unwrap() to change it. But you can
also use .read().unwrap() to get read access. It is
like RefCell because it follows the rules:

many .read() variables is okay,


one .write() variable is okay,
but more than one .read() or .read() together
with .write() is not okay.

The program will run forever if you try to .write()


when you can't get access:

use std::sync::RwLock;

fn main() {
let my_rwlock = RwLock::new(5);

let read1 = my_rwlock.read().unwrap(); // one .read() is fine


let read2 = my_rwlock.read().unwrap(); // two .read()s is al

println!("{:?}, {:?}", read1, read2);

let write1 = my_rwlock.write().unwrap(); // uh oh, now the p


}

So we use std::mem::drop , just like in a Mutex .

use std::sync::RwLock;
use std::mem::drop; // We will use drop() many times

fn main() {
let my_rwlock = RwLock::new(5);

let read1 = my_rwlock.read().unwrap();


let read2 = my_rwlock.read().unwrap();

println!("{:?}, {:?}", read1, read2);

138 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

drop(read1);
drop(read2); // we dropped both, so we can use .write() now

let mut write1 = my_rwlock.write().unwrap();


*write1 = 6;
drop(write1);
println!("{:?}", my_rwlock);
}

And you can use try_read() and try_write() too.

use std::sync::RwLock;

fn main() {
let my_rwlock = RwLock::new(5);

let read1 = my_rwlock.read().unwrap();


let read2 = my_rwlock.read().unwrap();

if let Ok(mut number) = my_rwlock.try_write


*number += 10;
println!("Now the number is {}", number);
} else {
println!("Couldn't get write access, sorry!"
};
}

Cow
Cow is a very convenient enum. It means "clone on
write" and lets you return a &str if you don't need a
String , and a String if you need it. (It can also do
the same with arrays vs. Vecs, etc.)

To understand it, let's look at the signature. It says:

pub enum Cow<'a, B>

139 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

where
B: 'a + ToOwned + ?Sized,
{
Borrowed(&'a B),
Owned(<B as ToOwned>::Owned),
}

You know right away that 'a means it works with


references. The ToOwned trait means that it is a type
that can be turned into an owned type. For example,
str is usually a reference ( &str ) and you can turn it
into an owned String .

Next is ?Sized . This means "maybe Sized, but maybe


not". Almost every type in Rust is Sized, but types like
str are not. That is why we need a & for a str ,
because the compiler doesn't know the size. So if you
want a trait that can use something like a str , you add
?Sized.

Next are enum variants. They are Borrowed and


Owned .

Imagine that you have a function that returns


Cow<'static, str> . If you tell the function to return
"My message".into() , it will look at the type: "My
message" is a str . This is a Borrowed type, so it
chooses Borrowed(&'a B) . So it becomes
Cow::Borrowed(&'static str) .

And if you give it a format!("{}", "My


message".into() then it will look at the type. This time
it is a String , because format! makes a String .
So this time it will select "Owned".

140 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Here is an example to test Cow . We will put a number


into a function that returns a Cow<'static, str> .
Depending on the number, it will create a &str or a
String . Then it uses .into() to turn it into a Cow .
When you do that, it will choose either
Cow:::Borrowed or Cow::Owned . Then we will match
to see which one it chose.

use std::borrow::Cow;

fn modulo_3(input: u8) -> Cow<'static, str> {


match input % 3 {
0 => "Remainder is 0".into(),
1 => "Remainder is 1".into(),
remainder => format!("Remainder is {}", remainder).
}
}

fn main() {
for number in 1..=6 {
match modulo_3(number) {
Cow::Borrowed(message) => println!(
Cow::Owned(message) => println!("{} went in. The Cow
}
}
}

This prints:

1 went in. The Cow is borrowed with this


message: Remainder is 1
2 went in. The Cow is owned with this
message: Remainder is 2
3 went in. The Cow is borrowed with this
message: Remainder is 0
4 went in. The Cow is borrowed with this
message: Remainder is 1
5 went in. The Cow is owned with this
message: Remainder is 2
6 went in. The Cow is borrowed with this

141 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

message: Remainder is 0

Cow has some other methods like into_owned or


into_borrowed so you can change it if you need to.

Type aliases
A type alias means "giving a new name to another
type". Type aliases are very easy. Usually you use them
when you have a very long type and don't want to write
it every time. It is also good when you want to give a
type a better name that is easy to remember. Here are
two examples of type aliases.

The type is not difficult, but you want to make your code
easier to understand for other people (or for you):

type CharacterVec = Vec<char>;

The type is very difficult to read:

// this return type is extremely long


fn returns<'a>(input: &'a Vec<char>) -> std::iter::Take<std::ite
input.iter().skip(4).take(5)
}

So you can change it to this:

type SkipFourTakeFive<'a> = std::iter::Take<std::iter::Skip<std:

fn returns<'a>(input: &'a Vec<char>) -> SkipFourTakeFive {


input.iter().skip(4).take(5)
}

142 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Of course, you can also import items to make the type


shorter:

use std::iter::{Take, Skip};


use std::slice::Iter;

fn returns<'a>(input: &'a Vec<char>) -> Take<Skip<Iter<


input.iter().skip(4).take(5)
}

So you can decide what looks best in your code


depending on what you like.

Note that this doesn't create a new type. If you write


type File = String; , the compiler just sees a
String . So this will print true :

type File = String;

fn main() {
let my_file = File::from("I am file contents"
let my_string = String::from("I am file contents"
println!("{}", my_file == my_string);
}

If you want a new file type that the compiler sees as a


File , you can put it in a struct:

struct File(String); // File is a wrapper around String

fn main() {
let my_file = File(String::from("I am file contents"
let my_string = String::from("I am file contents"
}

Now this will not work, because they are two different
types:

143 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

println!("{}", my_file == my_string); // cannot compare File with

If you want to compare the String inside, you can use


my_file.0:

struct File(String);

fn main() {
let my_file = File(String::from("I am file contents"
let my_string = String::from("I am file contents"
println!("{}", my_file.0 == my_string); // my_file.0 is a Stri
}

Importing inside a function

Usually you write use at the top of the program, like


this:

use std::cell::{Cell, RefCell};

But you can do this anywhere. Sometimes you see this


in functions with enums with long names. Here is an
example.

enum MapDirection {
North,
NorthEast,
East,
SouthEast,
South,
SouthWest,
West,
NorthWest,
}

fn main() {

144 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

fn give_direction(direction: &MapDirection) {
match direction {
MapDirection::North => println!("You are heading north."
MapDirection::NorthEast => println!("You are heading nor
// So much more left to type...
}
}

So now we will import MapDirection inside the function.


That means that inside the function you can just write
North and so on.

enum MapDirection {
North,
NorthEast,
East,
SouthEast,
South,
SouthWest,
West,
NorthWest,
}

fn main() {

fn give_direction(direction: &MapDirection) {
use MapDirection::*; // Import everything in MapDirection
let m = "You are heading";

match direction {
North => println!("{} north.", m),
NorthEast => println!("{} northeast.", m),
// This is a bit better
}
}

145 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

You can also use as to change the name. For


example, maybe you are using someone else's code
and you can't change the names in an enum:

enum FileState {
CannotAccessFile,
FileOpenedAndReady,
NoSuchFileExists,
SimilarFileNameInNextDirectory,
}

So then you can 1) import everything and 2) change the


names:

enum FileState {
CannotAccessFile,
FileOpenedAndReady,
NoSuchFileExists,
SimilarFileNameInNextDirectory,
}

fn give_filestate(input: &FileState) {
use FileState::{
CannotAccessFile as NoAccess,
FileOpenedAndReady as Good,
NoSuchFileExists as NoFile,
SimilarFileNameInNextDirectory as OtherDirectory
};
match input {
NoAccess => println!("Can't access file."
Good => println!("Here is your file"),
NoFile => println!("Sorry, there is no file by that name.
OtherDirectory => println!("Please check the other direc
}
}

So now you can write OtherDirectory instead of


FileState::SimilarFileNameInNextDirectory .

146 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

The todo! macro


Sometimes you want to write code in general to help
you imagine your project. For example, imagine a
simple project to do something with books. Here's what
you think as you write it:

struct Book {} // Okay, first I need a book struct.


// Nothing in there yet - will add later

enum BookType { // A book can be hardcover or softcover, so add


HardCover,
SoftCover,
}

fn get_book(book: &Book) -> Option<String> {} // get_book should

fn delete_book(book: Book) -> Result<(), String

fn check_book_type(book_type: &BookType) { // Let's make sure th


match book_type {
BookType::HardCover => println!("It's hardcover"
BookType::SoftCover => println!("It's softcover"
}
}

fn main() {
let book_type = BookType::HardCover;
check_book_type(&book_type); // Okay, let's check this funct
}

But Rust is not happy with get_book and


delete_book . It says:

error[E0308]: mismatched types


--> src\main.rs:32:29
|
32 | fn get_book(book: &Book) ->
Option<String> {}

147 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

| --------
^^^^^^^^^^^^^^ expected enum
`std::option::Option`, found `()`
| |
| implicitly returns `()` as its body
has no tail or `return` expression
|
= note: expected enum
`std::option::Option<std::string::String>`
found unit type `()`

error[E0308]: mismatched types


--> src\main.rs:34:31
|
34 | fn delete_book(book: Book) ->
Result<(), String> {}
| -----------
^^^^^^^^^^^^^^^^^^ expected enum
`std::result::Result`, found `()`
| |
| implicitly returns `()` as its body
has no tail or `return` expression
|
= note: expected enum
`std::result::Result<(),
std::string::String>`
found unit type `()`

But you don't care about get_book and delete_book


right now. This is where you can use todo!() . If you
add that to the function, Rust will not complain, and will
compile.

fn get_book(book: &Book) -> Option<String> {


todo!() // todo means "I will do it later, please be quiet"
}

fn delete_book(book: Book) -> Result<(), String


todo!()
}

148 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

So now the code compiles and you can see the result of
check_book_type : It's hardcover .

If you call a function with todo!() inside it, it will panic.

Also, todo!() functions still need real input and output


types. If you just write this, it will not compile:

fn get_book(book: &Book) -> WorldsBestType {


todo!()
}

It will say:

error[E0412]: cannot find type


`WorldsBestType` in this scope
--> src\main.rs:32:29
|
32 | fn get_book(book: &Book) ->
WorldsBestType {
|
^^^^^^^^^^^^^^ not found in this scope

todo!() is actually the same as another macro:


unimplemented!() . Programmers were using
unimplemented() a lot but it was long to type, so they
created todo!() which is shorter.

Rc
Rc means "reference counter". You know that in Rust,
every variable can only have one owner. That is why
this doesn't work:

fn main() {
let user_name = String::from("User MacUserson"

149 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

takes_a_string(user_name);
also_takes_a_string(user_name);
}

fn takes_a_string(input: String) {
println!("It is: {}", input)
}

fn also_takes_a_string(input: String) {
println!("It is: {}", input)
}

After takes_a_string takes user_name , you can't use


it anymore. Here that is no problem: you can just give it
user_name.clone() . But sometimes a variable is part
of a struct, and maybe you can't clone the struct. Or
maybe the String is really long and you don't want to
clone it. These are some reasons for Rc , which lets
you have more than one owner. An Rc is like a good
office worker: Rc writes down who has ownership, and
how many. Then once the number of owners goes down
to 0, the variable can disappear.

Here's how you use an Rc . First imagine two structs:


one called City , and another called Cities . City
has information for one city, and Cities puts all the
cities together in Vec s.

#[derive(Debug)]
struct City {
name: String,
population: u32,
city_history: String,
}

#[derive(Debug)]
struct Cities {
names: Vec<String>,

150 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

histories: Vec<String>,
}

fn main() {
let calgary = City {
name: "Seoul".to_string(),
population: 1_200_000,
// Pretend that this string is very very long
city_history: "Calgary began as a fort called Fort Calga
};

let canada_cities = CityData {


names: vec![calgary.name], // This is using calgary.name
histories: vec![calgary.city_history],
};

println!("Calgary's history is: {}", calgary.city_history);


}

Of course, it doesn't work because canada_cities


now owns the data and calgary doesn't. We can clone
the name: names: vec![calgary.name.clone()] but
we don't want to clone the city_history , which is
long. So we can use an Rc .

Add the use declaration:

use std::rc::Rc;

Then put Rc around String .

#[derive(Debug)]
struct City {
name: String,
population: u32,
city_history: Rc<String>,
}

#[derive(Debug)]
struct Cities {

151 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

names: Vec<String>,
histories: <Vec<Rc<<String>>>,
}

To add a new reference, you have to clone the Rc .


You can clone an item with item.clone() or with
Rc::clone(&item) .

Now our code looks like this:

use std::rc::Rc;

#[derive(Debug)]
struct City {
name: String,
population: u32,
city_history: Rc<String>, // String inside an Rc
}

#[derive(Debug)]
struct CityData {
names: Vec<String>,
histories: Vec<Rc<String>>, // A Vec of Strings inside Rcs
}

fn main() {
let calgary = City {
name: "Seoul".to_string(),
population: 1_200_000,
// Pretend that this string is very very long
city_history: Rc::new("Calgary began as a fort called Fo
};

let canada_cities = CityData {


names: vec![calgary.name],
histories: vec![calgary.city_history.clone
};

println!("Calgary's history is: {}", calgary.city_history);


}

152 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

So calgary.city_history has 2 owners. We can check the


number of owners with Rc::strong_count(&item) .
Add this:

println!("{}", Rc::strong_count(&calgary.city_history));

This prints 2 .

Let's add a new owner:

let new_owner = calgary.city_history.clone();

new_owner is now an Rc<String> . Now if we use


println!("{}",
Rc::strong_count(&calgary.city_history)); , we get
3 .

So if there are strong pointers, are there weak pointers?


Yes, there are. Weak pointers are useful because if two
Rcs point at each other, they can't die. This is called a
"reference cycle". If item 1 has an Rc to item 2, and item
2 has an Rc to item 1, they can't get to 0. In this case
you want to use weak references. Rc will count the
references, but if it only has weak references then it can
die. You use Rc::downgrade(&item) instead of
Rc::clone(&item) to make weak references. Also, you
use Rc::weak_count(&item) to see the weak count.

Multiple threads
If you use multiple threads, you can do many things at
the same time. Rust uses threads that are called "OS
threads". OS thread means the operating system
creates the thread on a different core.

153 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

You create threads with std::thread::spawn and then


a closure to tell it what to do. Threads are interesting
because they run at the same time. Here is a simple
example:

fn main() {
std::thread::spawn(|| {
println!("I am printing something");
});
}

If you run this, it will be different every time. Sometimes


it will print, and sometimes it won't print. That is
because sometimes main() finishes before the thread
finishes. And when main() finishes, the program is
over. This is easier to see in a for loop:

fn main() {
for _ in 0..10 { // set up ten threads
std::thread::spawn(|| {
println!("I am printing something");
});
} // Now the threads start.
} // How many can finish before main() ends here?

Usually about four threads will print before main ends,


but it is always different. Also, sometimes the threads
will panic:

thread 'thread 'I am printing something


thread '<unnamed><unnamed>thread '' panicked
at '<unnamed>I am printing something
' panicked at 'thread '<unnamed>cannot
access stdout during shutdown' panicked at
'<unnamed>thread 'cannot access stdout
during
shutdown

154 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

This is the error when the thread tries to do something


right when the program is shutting down.

You can give the computer something to do so it won't


shut down right away:

fn main() {
for _ in 0..10 {
std::thread::spawn(|| {
println!("I am printing something");
});
}
for i in 0..1_000_000 { // make the program declare "let x =
let x = 9;
}
}

But that is a silly way to give the threads time to finish.


The better way is to bind the threads to a variable. If you
add let , then you will create a JoinHandle .

fn main() {
for _ in 0..10 {
let handle = std::thread::spawn(|| {
println!("I am printing something");
});

}
}

handle is now a JoinHandle , and it has the method


.join() . This method means "wait until all the threads
are done" (it waits for the threads to join it). So now just
write handle.join() and it will wait for all ten threads
to finish.

fn main() {
for _ in 0..10 {

155 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

let handle = std::thread::spawn(|| {


println!("I am printing something");
});

handle.join(); // Wait for the threads to finish


}
}

Now we will learn about the three types of closures. The


three types are:

FnOnce : takes the whole value

FnMut : takes a mutable reference

Fn : takes a regular reference

A closure will try to use Fn if it can. But if it needs to


change the value it will use FnMut , and if it needs to
take the whole value, it will use FnOnce . FnOnce is a
good name because it explains what it does: it takes the
value once, and then it can't take it again.

Here is an example:

fn main() {
let my_string = String::from("I will go into the closure"
let my_closure = || println!("{}", my_string);
my_closure();
my_closure();
}

String is not Copy , so my_closure() is Fn : it takes


a reference.

If we change my_string , it will be FnMut .

fn main() {
let mut my_string = String::from("I will go into the closure
let mut my_closure = || {

156 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

my_string.push_str(" now");
println!("{}", my_string);
};
my_closure();
my_closure();
}

This prints:

I will go into the closure now


I will go into the closure now now

And if you take by value, then it will be FnOnce .

fn main() {
let my_vec: Vec<i32> = vec![8, 9, 10];
let my_closure = || {
my_vec
.into_iter() // into_iter takes ownership
.map(|x| x as u8) // turn it into u8
.map(|x| x * 2) // multiply by 2
.collect::<Vec<u8>>() // collect into a Vec
};
let new_vec = my_closure();
println!("{:?}", new_vec);
}

We took by value, so we can't run my_closure() more


than once. That is where the name comes from.

So now back to threads. Let's try to use a value from


outside:

fn main() {
let mut my_string = String::from("Can I go inside the thread

let handle = std::thread::spawn(|| {


println!("{}", my_string);
});

157 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

handle.join();
}

The compiler says that this won't work.

error[E0373]: closure may outlive the


current function, but it borrows
`my_string`, which is owned by the current
function
--> src\main.rs:28:37
|
28 | let handle = std::thread::spawn(||
{
| ^^
may outlive borrowed value `my_string`
29 | println!("{}", my_string);
| ---------
`my_string` is borrowed here
|
note: function requires argument type to
outlive `'static`
--> src\main.rs:28:18
|
28 | let handle =
std::thread::spawn(|| {
| __________________^
29 | | println!("{}", my_string);
30 | | });
| |______^
help: to force the closure to take ownership
of `my_string` (and any other referenced
variables), use the `move` keyword
|
28 | let handle =
std::thread::spawn(move || {
|
^^^^^^^

158 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

It is a long message, but helpful: it says to use the


`move` keyword . The problem is that we can do
anything to my_string while the thread is using it. That
would be unsafe.

fn main() {
let mut my_string = String::from("Can I go inside the thread

let handle = std::thread::spawn(|| {


println!("{}", my_string); // now my_string is being use
});

std::mem::drop(my_string); // Maybe we drop it. But the thre

handle.join();
}

So you have to take the value with move . Now it is


safe:

fn main() {
let mut my_string = String::from("Can I go inside the thread

let handle = std::thread::spawn(move|| {


println!("{}", my_string);
});

std::mem::drop(my_string); // we can't drop, because handle

handle.join();
}

So we delete the std::mem::drop , and now it is okay.


handle takes my_string and our code is safe.

fn main() {
let mut my_string = String::from("Can I go inside the thread

let handle = std::thread::spawn(move|| {

159 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

println!("{}", my_string);
});

handle.join();
}

So just remember: if you need a value in a thread from


outside the thread, you need to use move .

Arc
You remember that we used an Rc to give a variable
more than one owner. If we are doing the same thing in
a thread, we need an Arc . Arc means "atomic
reference counter". Atomic means that it uses the
computer's processor so that data only gets written
once each time. This is important because if two
threads write data at the same time, you will get the
wrong result. For example, imagine if you could do this
in Rust:

let mut x = 10;

for i in 0..10 { // Thread 1


x += 1
}
for i in 0..10 { // Thread 2
x += 1
}

If Thread 1 and Thread 2 just start together, maybe this


will happen:

Thread 1 sees 10, writes 11. Then Thread 2 sees


11, writes 12. No problem so far.
Thread 1 sees 12. At the same time, Thread 2 sees
12. Thread 1 writes 13. And Thread 2 writes 13.

160 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Now we have 13, but it should be 14.

An Arc uses the processor to make sure this doesn't


happen, so it is the method you must use when you
have threads. You don't want an Arc for just one
thread though, because Rc is a bit faster.

You can't change data with just an Arc though. So you


wrap the data in a Mutex , and then you wrap the
Mutex in an Arc .

So let's use a Mutex inside an Arc to change the


value of a number. First let's set up one thread:

fn main() {

let handle = std::thread::spawn(|| {


println!("The thread is working!") // Just testing the t
});

handle.join().unwrap(); // Make the threads wait here until


println!("Exiting the program");
}

Good. Now let's put it in a for loop for 0..10 :

fn main() {

let handle = std::thread::spawn(|| {


for _ in 0..10 {
println!("The thread is working!")
}
});

handle.join().unwrap();
println!("Exiting the program");
}

161 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Now let's make one more thread. Each thread will do


the same thing. You can see that the threads are
working at the same time. Sometimes it will say
Thread 1 is working! first, but other times Thread
1 is working! is first. This is called concurrency,
which means "running together".

fn main() {

let thread1 = std::thread::spawn(|| {


for _ in 0..10 {
println!("Thread 1 is working!")
}
});

let thread2 = std::thread::spawn(|| {


for _ in 0..10 {
println!("Thread 2 is working!")
}
});

thread1.join().unwrap();
thread2.join().unwrap();
println!("Exiting the program");
}

Now we want to change the value of my_number . Right


now it is an i32 . We will change it to an
Arc<Mutex<i32>> : an i32 that can be changed,
protected by an Arc .

let my_number = Arc::new(Mutex::new(0));

Now that we have this, we can clone it. Each clone can
go into a different thread. We have two threads, so we
will make two clones:

let my_number = Arc::new(Mutex::new(0));

162 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

let my_number1 = Arc::clone(&my_number); // This clone goes into


let my_number2 = Arc::clone(&my_number); // This clone goes into

Now that we have safe clones attached to my_number ,


we can move them into other threads with no problem.

use std::sync::{Arc, Mutex};

fn main() {
let my_number = Arc::new(Mutex::new(0));

let my_number1 = Arc::clone(&my_number);


let my_number2 = Arc::clone(&my_number);

let thread1 = std::thread::spawn(move || {


for _ in 0..10 {
*my_number1.lock().unwrap() +=1; // Lock the Mutex,
}
});

let thread2 = std::thread::spawn(move || {


for _ in 0..10 {
*my_number2.lock().unwrap() += 1;
}
});

thread1.join().unwrap();
thread2.join().unwrap();
println!("Value is: {:?}", my_number);
println!("Exiting the program");
}

The program prints:

Value is: Mutex { data: 20 }


Exiting the program

So it was a success.

163 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

Then we can join the two threads together in a single


for loop, and make the code smaller.

use std::sync::{Arc, Mutex};

fn main() {
let my_number = Arc::new(Mutex::new(0));

for _ in 0..2 { // do this twice


let my_number_clone = Arc::clone(&my_number);
let handle = std::thread::spawn(move ||
for _ in 0..10 {
*my_number_clone.lock().unwrap()
}
});
handle.join().unwrap(); // wait here
}

println!("{:?}", my_number);
}

This looks complicated but Arc<Mutex>> is used very


often in Rust, so it becomes natural. Also, you can
always write your code to make it cleaner. Here is the
same code with one more use statement and two
functions. The functions don't do anything new, but they
move some code out of main() . You can try rewriting
code like this if it is hard to read.

use std::sync::{Arc, Mutex};


use std::thread::spawn; // Now we just write spawn

fn make_arc(number: i32) -> Arc<Mutex<i32>> { // Just a function


Arc::new(Mutex::new(number))
}

fn new_clone(input: &Arc<Mutex<i32>>) -> Arc<Mutex<


Arc::clone(&input)
}

164 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

// Now main() is easier to read

fn main() {
let my_number = make_arc(0);

for _ in 0..2 {
let my_number_clone = new_clone(&my_number);
let handle = spawn(move || {
for _ in 0..10 {
let mut value_inside = my_number_clone.
*value_inside += 1; // Now it is clear that the
}
});
handle.join().unwrap();
}

println!("{:?}", my_number);
}

You can also make a vector of handles, and use


.join().unwrap() on them. Then you can do what
you want with each handle inside. Here is main() with
the handles in a vector:

fn main() {
let mut handle_vec = vec![]; // each handle will go in here
let my_number = make_arc(0);

for _ in 0..2 {
let my_number_clone = new_clone(&my_number);
let handle = spawn(move || {
for _ in 0..10 {
let mut value_inside = my_number_clone.
*value_inside += 1;
}
});
handle_vec.push(handle); // the handle is done, so pu
}

handle_vec.into_iter().for_each(|handle| handle.

165 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

println!("{:?}", my_number);
}

Channels
A channel is an easy way to use many threads that
send to one place. You can create a channel in Rust
with std::sync::mpsc . mpsc means "multiple
producer, single consumer", so "many threads sending
to one place". To start a channel, you use channel() .
This creates a Sender and a Receiver that are tied
together. You can see this in the function signature:

pub fn channel<T>() -> (Sender<T>, Receiver<T>)

So you have to choose one name for the sender and


one for the receiver. Usually you see let (sender,
receiver) = channel(); to start. Because it's generic,
Rust won't know the type if that is all you write:

use std::sync::mpsc::channel;

fn main() {
let (sender, receiver) = channel();
}

The compiler says:

error[E0282]: type annotations needed for


`(std::sync::mpsc::Sender<T>,
std::sync::mpsc::Receiver<T>)`
--> src\main.rs:30:30
|
30 | let (sender, receiver) = channel();
| ------------------ ^^^^^^^

166 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

cannot infer type for type parameter `T`


declared on the function `channel`
| |
| consider giving this pattern
the explicit type
`(std::sync::mpsc::Sender<T>,
std::sync::mpsc::Receiver<T>)`, where
the type parameter `T` is specified

It suggests adding a type for the Sender and


Receiver . You can do that if you want:

use std::sync::mpsc::{channel, Sender, Receiver};

fn main() {
let (sender, receiver): (Sender<i32>, Receiver
}

but you don't have to. Once you start using the Sender
and Receiver , Rust can guess the type.

So let's look at the simplest way to use a channel.

use std::sync::mpsc::channel;

fn main() {
let (sender, receiver) = channel();

sender.send(5);
receiver.recv();
}

Now the compiler knows the type. sender is a


Result<(), SendError<i32>> and receiver is a
Result<i32, RecvError . So you can use unwrap to
see if the sending works, or use better error handling.
Let's add .unwrap() and also println! to see what
we get:

167 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

use std::sync::mpsc::channel;

fn main() {
let (sender, receiver) = channel();

sender.send(5).unwrap();
println!("{}", receiver.recv().unwrap());
}

This prints 5 .

A channel is like an Arc because you can clone it


and send the clones into other threads. Let's make two
threads and send values to receiver . This code will
work, but it is not exactly what we want.

use std::sync::mpsc::channel;

fn main() {
let (sender, receiver) = channel();
let sender_clone = sender.clone();

std::thread::spawn(move|| { // move sender in


sender.send("Send a &str this time").unwrap
});

std::thread::spawn(move|| { // move sender_clone in


sender_clone.send("And here is another &str"
});

println!("{}", receiver.recv().unwrap());
}

The two threads start sending, and then we println! .


Sometimes it will say Send a &str this time and
sometimes it will say And here is another &str .
Let's make a join handle to make them wait.

use std::sync::mpsc::channel;

168 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

169 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

170 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

171 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

172 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

173 of 174 7/23/20, 9:22 PM


Dhghomon/easy_rust: Rust explained using easy English https://github.com/Dhghomon/easy_rust

174 of 174 7/23/20, 9:22 PM

You might also like