Dhghomon: Easy - Rust
Dhghomon: Easy - Rust
com/Dhghomon/easy_rust
Dhghomon / easy_rust
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
Rust Playground
Types
Primitive types
Signed integers,
Unsigned integers.
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);
fn main() {
Chars
A char is one character. For a char , use '' instead
of "" .
fn main() {
let slice = "Hello!";
Type inference
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
}
fn main() {
fn main() {
let number = 0________u8;
let number2 = 1___6______2____4______i32;
println!("{}, {}", number, number2);
}
Floats
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 .
fn main() {
let my_float: f64 = 5.0; // This is an f64
fn main() {
let my_float: f64 = 5.0;
let my_other_float: f32 = 8.5;
fn main() {
let my_float = 5.0; // Rust will choose f64
let my_other_float = 8.5; // Here again it will choose f64
fn main() {
let my_float: f32 = 5.0;
let my_other_float = 8.5; // Rust will choose f32,
fn main() {
println!("Hello, world!");
}
fn means function,
{} is a code block.
fn main() {
println!("Hello, world number {}!", 8);
}
fn main() {
println!("Hello, worlds number {} and {}!",
}
fn main() {
println!("Hello, world number {}", number());
}
fn main() {
println!("Hello, world number {}", number());
}
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 main() {
let multiply_result = multiply(8, 9); // We used multiply()
}
fn main() {
let my_number = 8;
println!("Hello, number {}", my_number);
}
fn main() {
{
let my_number = 8; // my_number starts here
// my_number ends here!
}
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
};
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
};
fn main() {
let doesnt_print = ();
println!("This will not print: {}", doesnt_print);
}
`std::fmt::Display::fmt`
= note: this error originates in a macro
(in Nightly builds, run with -Z macro-
backtrace for more info)
fn main() {
print!("This will not print a new line");
println!(" so this will be on the same line"
}
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 {}."
Mutability (changing)
When you declare a variable with let , it is immutable
(cannot be changed).
fn main() {
let my_number = 8;
my_number = 10;
}
fn main() {
let mut my_number = 8;
my_number = 10;
}
fn main() {
let mut my_variable = 8;
my_variable = "Hello, world!";
}
Shadowing
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
}
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
}
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 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)
}
The stack and the heap are two places to keep memory.
The important differences are:
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
fn main() {
let number = 9;
let number_ref = &number;
println!("{:p}", number_ref);
}
fn main() {
let number = 555;
println!("Binary: {:b}, hexadecimal: {:x}, octal: {:o}"
}
fn main() {
let father_name = "Vlad";
let son_name = "Adrian Fahrenheit";
let family_name = "Țepeș";
println!("This is {1} {2}, son of {0} {2}."
}
fn main() {
println!("{city1} is in {country} and {city2} is also in {co
but {city3} is not in {country}.", city1 = "Seoul"
}
ㅎ
ㅎ
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);
ㅎ
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
ㅎ
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.
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?
fn main() {
let name = " 서태지
"; // This is a Korean name. No problem,
let other_name = String::from("Adrian Fahrenheit Țepeș"
}
fn main() {
�";
let name = "�
println!("My name is actually {}", name);
}
서태지
= the size can be different). For example, the
names " " and "Adrian Fahrenheit Țepeș"
are not the same size on the stack:
fn main() {
fn main() {
let my_name = "Billybrobby";
let my_country = "USA";
let my_home = "Korea";
fn main() {
let my_string = "Try to make this a String"
fn main() {
let my_string: String = "Try to make this a String"
}
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);
}
fn main() {
let country = return_str();
}
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;
}
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);
}
fn main() {
let mut number = 10;
let number_ref = &number;
let number_change = &mut number;
*number_change += 10;
println!("{}", number_ref);
}
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
}
Shadowing again
fn main() {
let country = String::from("Austria");
let country_ref = &country;
let country = 8;
println!("{}, {}", country_ref, country);
}
Giving references to
functions
References are very useful for functions. The rule in
Rust on variables is: a variable can only have one
owner.
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);
}
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);
}
fn main() {
let mut country = String::from("Austria");
add_hungary(&mut country);
}
So to conclude:
fn main() {
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.
https://doc.rust-lang.org/std/primitive.char.html
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
}
https://doc.rust-lang.org/std/string/struct.String.html
fn main() {
let country = String::from("Kiribati");
prints_country(country);
prints_country(country);
}
fn prints_country(country_name: String) {
println!("{}", country_name);
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);
}
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 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);
}
let my_variable;
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
};
my_number = loop_then_return(number);
}
println!("{}", my_number);
}
fn main() {
let my_number;
{
my_number = 50;
}
println!("{}", my_number);
}
Collection types
Here are some types for making a collection.
Arrays
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
}
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);
}
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
}
fn main() {
let array_of_ten = [1, 2, 3, 4, 5, 6, 7, 8,
Remember that:
Vectors
fn main() {
let name1 = String::from("Windy");
let name2 = String::from("Gomesy");
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.
fn main() {
let mut my_vec = vec![8, 10, 10];
}
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[..];
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
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
}
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
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,
)
}
fn main() {
let str_vec = vec!["one", "two", "three"];
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");
}
}
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")
}
}
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"),
}
}
fn main() {
let my_number: u8 = 5;
match my_number {
0 => println!("it's zero"),
fn main() {
let my_number = 5;
let second_number = match my_number {
0 => 0,
5 => 10,
_ => 2,
};
}
fn main() {
let sky = "cloudy";
let temperature = "warm";
fn main() {
let children = 5;
let married = true;
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);
This prints:
fn main() {
let my_number = 10;
let some_variable = match my_number {
10 => 8,
_ => "Not ten",
};
}
| _________________________-
16 | | 10 => 8,
| | - this is found to be
of type `{integer}`
17 | | _ => "Not ten",
| | ^^^^^^^^^ expected
integer, found `&str`
18 | | };
| |_____- `match` arms have incompatible
types
fn main() {
let my_number = 10;
if my_number == 10 {
let some_variable = 8;
} else {
let some_variable = "Something else";
}
}
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
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).
struct FileDirectory;
fn main() {
let my_colour = Colour(50, 0, 50); // Make a colour out of R
println!("The second part of the colour is: {}"
}
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);
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"
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"
Enums
An enum is short for enumerations. They look similar to
a struct, but are different. Here is the difference:
enum ThingsInTheSky {
Sun,
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
}
enum ThingsInTheSky {
Sun(String), // Now each variant has a string
Stars(String),
}
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
}
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),
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
I32(-800)
U32(8)
fn main() {
let my_number = 9;
let reference = &my_number;
struct Item {
number: u8,
}
fn main() {
let item = Item {
number: 8,
};
struct Item {
number: u8,
}
fn main() {
let item = Item {
number: 8,
};
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,
};
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:
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
};
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 .
}
}
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;
}
}
}
fn main() {
let mut counter = 0;
let mut counter2 = 0;
println!("Now entering the first loop.");
counter +=1;
println!("The counter is now: {}", counter);
if counter > 9 { // Starts a second loop inside this loo
println!("Now entering the second loop."
fn main() {
let mut counter = 0;
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.
fn main() {
for number in 0..3 {
println!("The number is: {}", number);
}
fn main() {
for _ in 0..3 {
println!("Printing the same thing three times"
}
}
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 :
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);
}
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);
}
#[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"
#[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:
Self
Generics
In functions, you write what type to take as input:
fn main() {
let number = return_number(5);
}
fn return_number<T>(number: T) -> T {
println!("Here is your number.");
number
}
number
}
So now it works:
fn main() {
let number = return_number(5);
}
fn print_number<T>(number: T) {
println!("Here is your number: {:?}", number);
}
fn main() {
print_number(5);
}
fn print_number<T: Debug>(number: T) {
println!("Here is your number: {:?}", number);
}
fn main() {
print_number(5);
}
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,
};
print_item(charlie);
print_item(number);
}
This prints:
use std::fmt::Display;
use std::cmp::PartialOrd;
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:
use std::cmp::PartialOrd;
use std::fmt::Display;
where
T: Display,
U: Display + PartialOrd,
{
println!("{}! Is {} greater than {}? {}", statement, num_1,
}
fn main() {
compare_and_display("Listen up!", 9, 8);
}
Also note:
For example:
use std::fmt::Display;
fn main() {
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 main() {
let new_vec = vec![1, 2];
let bigger_vec = vec![1, 2, 3, 4, 5];
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 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
fn handle_option(my_option: Vec<Option<i32>>) {
for item in my_option {
match item {
Some(number) => println!("{}", number),
None => {}, // do nothing
}
}
}
enum Option<T> {
None,
Some(T),
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
}
}
}
Result
enum Option<T> {
None,
Some(T),
}
fn main() {
check_error();
}
fn main() {
let mut result_vec = Vec::new(); // Create a new vec for the
println!("{:?}", result_vec);
}
fn main() {
let error_value: Result<i32, &str> = Err("There was an error
println!("{}", error_value.unwrap()); // Unwrap it
}
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)
None
fn main() {
let my_vec = vec![2, 3, 4];
fn main() {
let my_vec = vec![2, 3, 4];
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.
fn main() {
let mut weather_vec = vec!["Berlin", "cloudy"
while let Some(information) = weather_vec.pop
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:
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);
}
}
This prints:
fn main() {
let failure = "Not a number".parse::<i32>();
failure.rbrbrb(); // Compiler: "What is rbrbrb()???"
}
So std::result::Result<i32,
std::num::ParseIntError> is the signature we need.
use std::num::ParseIntError;
use std::num::ParseIntError;
fn main() {
panic!("Time to panic!");
}
fn main() {
let my_vec = vec![8, 9, 10];
prints_three_things(my_vec);
}
fn prints_three_things(vector: Vec<i32>) {
println!("{}, {}, {}", vector[0], vector[1], vector[
}
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[
}
fn main() {
let my_vec = vec![8, 9, 10, 10, 55, 99];
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[
}
They mean:
Some examples:
fn main() {
let my_name = "Loki Laufeyson";
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
);
}
}
fn main() {
let my_name = "Mithridates";
assert_ne!(
my_name, "Mithridates",
"{} must not equal Mithridates",
my_name
);
}
It will display:
left: `"Mithridates"`,
right: `"Mithridates"`: Mithridates must
not equal Mithridates', src\main.rs:4:5
fn main() {
let my_vec = vec![9, 0, 10];
let fourth = get_fourth(&my_vec);
}
fn main() {
let my_vec = vec![9, 0, 10];
let fourth = get_fourth(&my_vec);
}
*fourth
}
fn main() {
let my_vec = vec![8, 9, 10];
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,
}
struct ThingsToAdd {
first_thing: u32,
second_thing: f32,
}
impl Dog for Animal {} // Now Animal has the trait Dog
fn main() {
let rover = Animal {
name: "Rover".to_string(),
};
fn run(&self) {
println!("The dog is running!");
}
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.
}
fn main() {
let rover = Animal {
name: "Rover".to_string(),
};
rover.bark();
rover.run();
}
struct Cat {
name: String,
age: u8,
}
fn main() {
let mr_mantle = Cat {
name: "Reggie Mantle".to_string(),
age: 4,
};
}
#[derive(Debug)]
struct Cat {
name: String,
age: u8,
}
fn main() {
let mr_mantle = Cat {
name: "Reggie Mantle".to_string(),
age: 4,
};
use std::fmt;
struct Position {
longitude: f32,
latitude: f32,
}
use std::fmt;
struct Cat {
name: String,
age: u8,
}
use std::fmt;
struct Cat {
name: String,
age: u8,
}
fn main() {
let mr_mantle = Cat {
name: "Reggie Mantle".to_string(),
age: 4,
};
println!("{}", mr_mantle);
}
https://doc.rust-lang.org/std/string/struct.String.html
AsRef<str> :
AsRef<[u8]> :
AsRef<OsStr> :
fn print_it<T>(input: T) {
println!("{}", input)
}
fn main() {
print_it("Please print me");
}
use std::fmt::Display;
fn print_it<T: Display>(input: T) {
println!("{}", input)
}
fn main() {
print_it("Please print me");
}
use std::fmt::Display;
fn main() {
print_it("Please print me");
print_it("Also, please print me".to_string());
// print_it(7); <- This will not print
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;
counter += 1;
}
println!("{:?}", new_vec);
}
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);
}
fn main() {
let my_vec = vec![0, 1, 2, 3, 4, 5, 6, 7, 8
println!("{:?}", new_vec);
}
fn main() {
println!("{:?}", new_vec);
}
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:
fn main() {
let vector1 = vec![1, 2, 3]; // for .iter() and .into_iter()
let mut vector2 = vec![10, 20, 30]; // for .iter_mut()
println!("{:?}", vector1_a);
println!("{:?}", vector2);
println!("{:?}", vector1_b);
fn main() {
let my_vec = vec!['a', 'b', ' 거 ', '柳'];
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
}
#[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 main() {
let mut my_library = Library::new(); // make a new library
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("吾輩は猫である");
It says:
#[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 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("吾輩は猫である");
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 () .
fn main() {
let my_closure = || println!("This is a closure"
my_closure();
}
fn main() {
let my_closure = |x: i32| println!("{}", x);
my_closure(5);
}
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();
}
fn main() {
let number_one = 6;
let number_two = 10;
fn main() {
let number_one = 6;
let number_two = 10;
fn main() {
let my_vec = vec![8, 9, 10];
println!("{}", fourth);
}
fn main() {
let num_vec = vec![2, 4, 6];
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:
fn main() {
let num_vec = vec![10, 9, 8];
num_vec
.iter()
.enumerate()
.map(|(index, number)| println!("Index number {} has num
It says:
|_| in a closure
fn main() {
let my_vec = vec![8, 9, 10];
println!("{:?}", my_vec.iter().for_each(||
}
fn main() {
let my_number = 8;
dbg!(my_number);
}
fn main() {
let mut my_number = 9;
my_number += 10;
fn main() {
let mut my_number = dbg!(9);
dbg!(my_number += 10);
dbg!(double_vec);
}
So this prints:
[src\main.rs:3] 9 = 9
and:
[src\main.rs:4] my_number += 10 = ()
and:
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,
fn main() {
let new_vec = vec![8, 9, 10];
fn main() {
let new_vec = vec![8, 9, 10];
This prints:
fn main() {
let new_vec = vec![8, 9, 10];
This prints:
Types of &str
There is more than one type of &str . We have:
fn main() {
let my_string = String::from("I am a string"
prints_str(&my_string); // we give prints_str a &String
}
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 main() {
let my_str = returns_str();
println!("{}", my_str);
}
Now it works:
fn main() {
let my_str = returns_str();
println!("{}", my_str);
}
#[derive(Debug)]
struct City {
name: &str,
date_founded: u32,
}
fn main() {
let my_city = City {
name: "Ichinomiya",
date_founded: 1921,
};
}
#[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,
};
#[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
#[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
#[derive(Debug)]
struct City<'city> { // The lifetime is now called 'city
name: &'city str, // and name has the 'city lifetime
date_founded: u32,
}
fn prints<T: Display>(input: T) {
println!("T is {}", input);
}
The same is true for lifetimes. When you write 'a here:
#[derive(Debug)]
struct City<'a> {
name: &'a str,
date_founded: u32,
}
Interior mutability
Cell
struct PhoneModel {
company_name: String,
model_name: String,
screen_size: f32,
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,
};
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),
};
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.
RefCell
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);
}
user_1.active.replace(false);
println!("{:?}", user_1.active);
user_1
.active
.replace_with(|_| if date < 2000 { true
println!("{:?}", user_1.active);
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),
};
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.
use std::sync::Mutex;
fn main() {
let my_mutex = Mutex::new(5); // A new Mutex<i32>. We don't
let mut mutex_changer = my_mutex.lock().unwrap
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
use std::sync::Mutex;
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
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
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
use std::sync::Mutex;
fn main() {
let my_mutex = Mutex::new(5);
*my_mutex.lock().unwrap() = 6;
println!("{:?}", my_mutex);
}
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
use std::sync::RwLock;
fn main() {
let my_rwlock = RwLock::new(5);
use std::sync::RwLock;
use std::mem::drop; // We will use drop() many times
fn main() {
let my_rwlock = RwLock::new(5);
drop(read1);
drop(read2); // we dropped both, so we can use .write() now
use std::sync::RwLock;
fn main() {
let my_rwlock = RwLock::new(5);
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.)
where
B: 'a + ToOwned + ?Sized,
{
Borrowed(&'a B),
Owned(<B as ToOwned>::Owned),
}
use std::borrow::Cow;
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:
message: Remainder is 0
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):
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);
}
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:
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
}
enum MapDirection {
North,
NorthEast,
East,
SouthEast,
South,
SouthWest,
West,
NorthWest,
}
fn main() {
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...
}
}
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
}
}
enum FileState {
CannotAccessFile,
FileOpenedAndReady,
NoSuchFileExists,
SimilarFileNameInNextDirectory,
}
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
}
}
fn main() {
let book_type = BookType::HardCover;
check_book_type(&book_type); // Okay, let's check this funct
}
| --------
^^^^^^^^^^^^^^ 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 `()`
So now the code compiles and you can see the result of
check_book_type : It's hardcover .
It will say:
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"
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)
}
#[derive(Debug)]
struct City {
name: String,
population: u32,
city_history: String,
}
#[derive(Debug)]
struct Cities {
names: Vec<String>,
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
};
use std::rc::Rc;
#[derive(Debug)]
struct City {
name: String,
population: u32,
city_history: Rc<String>,
}
#[derive(Debug)]
struct Cities {
names: Vec<String>,
histories: <Vec<Rc<<String>>>,
}
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
};
println!("{}", Rc::strong_count(&calgary.city_history));
This prints 2 .
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.
fn main() {
std::thread::spawn(|| {
println!("I am printing something");
});
}
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?
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;
}
}
fn main() {
for _ in 0..10 {
let handle = std::thread::spawn(|| {
println!("I am printing something");
});
}
}
fn main() {
for _ in 0..10 {
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();
}
fn main() {
let mut my_string = String::from("I will go into the closure
let mut my_closure = || {
my_string.push_str(" now");
println!("{}", my_string);
};
my_closure();
my_closure();
}
This prints:
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);
}
fn main() {
let mut my_string = String::from("Can I go inside the thread
handle.join();
}
fn main() {
let mut my_string = String::from("Can I go inside the thread
handle.join();
}
fn main() {
let mut my_string = String::from("Can I go inside the thread
handle.join();
}
fn main() {
let mut my_string = String::from("Can I go inside the thread
println!("{}", my_string);
});
handle.join();
}
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:
fn main() {
fn main() {
handle.join().unwrap();
println!("Exiting the program");
}
fn main() {
thread1.join().unwrap();
thread2.join().unwrap();
println!("Exiting the program");
}
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:
fn main() {
let my_number = Arc::new(Mutex::new(0));
thread1.join().unwrap();
thread2.join().unwrap();
println!("Value is: {:?}", my_number);
println!("Exiting the program");
}
So it was a success.
fn main() {
let my_number = Arc::new(Mutex::new(0));
println!("{:?}", my_number);
}
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);
}
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.
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:
use std::sync::mpsc::channel;
fn main() {
let (sender, receiver) = channel();
}
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.
use std::sync::mpsc::channel;
fn main() {
let (sender, receiver) = channel();
sender.send(5);
receiver.recv();
}
use std::sync::mpsc::channel;
fn main() {
let (sender, receiver) = channel();
sender.send(5).unwrap();
println!("{}", receiver.recv().unwrap());
}
This prints 5 .
use std::sync::mpsc::channel;
fn main() {
let (sender, receiver) = channel();
let sender_clone = sender.clone();
println!("{}", receiver.recv().unwrap());
}
use std::sync::mpsc::channel;