Move
// on stack
let x = 5;
let y = x;
println!("{} {}", x, y); // i32 implements Copy trait, therefore this is fine
// on heap
let s1 = String::from("hello");
let s2 = s1;
println!("s1 = {} s2 = {}", s1, s2); // error: borrow of moved value: `s1`
Clone
[//if](//if) we want to duplicate anything on heap, we need to call clone() explicitly
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
Returning ownership
fn main() {
let s1 = String::from("hello");
let (s2, len) = calculate_length(s1);
println!("The length of '{}' is {}.", s2, len);
}
fn calculate_length(s: String) -> (String, usize) {
let length = s.len(); // len() returns the length of a String
(s, length)
}
Reference
fn main() {
let s1 = String::from("hello");
let len = calculate_length(&s1);
println!("The length of '{}' is {}.", s1, len);
}
fn calculate_length(s: &String) -> usize {
s.len()
}
Mutable reference
fn main() {
let mut s = String::from("hello");
change(&mut s);
}
fn change(some_string: &mut String) {
some_string.push_str(", world");
}
Basic rules
- Unlimited number of references
- ONLY ONE mutable reference
- Reference must be valid/alive/existing
let mut s = String::from("hello");
let r1 = &s; // ok
let r2 = &s; // ok
let r3 = &mut s; // error: mutable borrow occurs here
println!("{}, {}, and {}", r1, r2, r3);
...but
let mut s = String::from("hello");
let r1 = &s; // ok
let r2 = &s; // ok
println!("{} and {}", r1, r2);
// r1 and r2 are not used any more
let r3 = &mut s; // ok
println!("{}", r3);
Slice
let s = String::from("hello world");
let hello = &s[0..5]; // hello
let world = &s[6..11]; // world
Working with string references
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
Structures
Definition
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
Usage
fn main() {
let mut user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
user1.email = String::from("anotheremail@example.com");
}
Shortcut for struct creation
fn build_user(email: String, username: String) -> User {
User {
email,
username,
active: true,
sign_in_count: 1,
}
}
Creation from already existing struct
fn main() {
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
let user2 = User {
email: String::from("another@example.com"),
username: String::from("anotherusername567"),
..user1
};
}
Working with struct
#[derive(Debug)] // 'Debug' macro allows us to print the struct
struct Rectangle {
width: u32,
height: u32,
}
fn main() {
let rect1 = Rectangle {
width: 30,
height: 50,
};
println!(
"The area of the rectangle is {} square pixels.",
area(&rect1)
);
println!("rect1 is {:?}", rect1); // here we use {:?} instead of empty {}
}
fn area(rectangle: &Rectangle) -> u32 {
rectangle.width * rectangle.height
}
Methods
impl Rectangle {
fn area(&self) -> u32 {
self.width * self.height
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width >= other.width && self.height >= other.height
}
}
impl Rectangle {
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
}
fn main() {
let sq = Rectangle::square(3);
let rect = Rectangle {
width: 3,
height: 4,
};
println!("Can rect hold sq? {}", rect.can_hold(&sq));
}
Enums
Simple enum
enum Delivery {
Pickup,
Parcel,
}
let delivery = Delivery::Pickup;
Enums like tuple structs
enum Delivery {
Pickup,
Parcel(String),
}
let delivery = Delivery::Parcel(String::from("Queens, New York 11434"));
Enums as c-like structures
// starting with 0
enum Number {
Zero,
One,
Two,
}
enum AnotherNumber {
Zero, // 0
Three = 3, // 3
Four, // 4
}
// enums with set value
enum Color {
Red = 0xff0000,
Green = 0x00ff00,
Blue = 0x0000ff,
}
fn main() {
// enum can be converted to integer
println!("zero is {}", Number::Zero as i32);
println!("one is {}", Number::One as i32);
println!("roses are #{:06x}", Color::Red as i32);
println!("violets are #{:06x}", Color::Blue as i32);
}
Pattern matching
enum Delivery {
Pickup,
Parcel(String),
PickInWarehouse,
}
fn main() {
let delivery = Delivery::Pickup;
deliver(delivery);
}
fn deliver(delivery: Delivery) {
match delivery {
Delivery::Pickup => {
println!("Pick the parcel at our shop!");
}
Delivery::Parcel(address) => {
println!("Parcel will be delivered to address: {}!", address);
}
_ => {
println!("Not implemented delivery.");
}
}
}
More pattern matching!
fn main() {
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got 50"),
Some(y) => println!("Matched, y = {:?}", y),
_ => println!("Default case, x = {:?}", x),
}
println!("at the end: x = {:?}, y = {:?}", x, y);
}
Enums pattern matching
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let msg = Message::ChangeColor(0, 160, 255);
match msg {
Message::Quit => {
println!("The Quit variant has no data to destructure.")
}
Message::Move { x, y } => {
println!(
"Move in the x direction {} and in the y direction {}",
x, y
);
}
Message::Write(text) => println!("Text message: {}", text),
Message::ChangeColor(r, g, b) => println!(
"Change the color to red {}, green {}, and blue {}",
r, g, b
),
}
}
Multiple options
fn main() {
let x = 1;
match x {
1 | 2 => println!("one or two"),
3 => println!("three"),
_ => println!("anything"),
}
}
Range
fn main() {
let x = 'c';
match x {
'a'..='j' => println!("early ASCII letter"),
'k'..='z' => println!("late ASCII letter"),
_ => println!("something else"),
}
}
// prints: early ASCII letter
Touple
// '..' can only be used once per tuple pattern
fn main() {
let numbers = (2, 4, 8, 16, 32);
match numbers {
(first, .., last) => {
println!("Some numbers: {}, {}", first, last);
}
}
}
Structs
fn main() {
struct Point {
x: i32,
y: i32,
z: i32,
}
let origin = Point { x: 0, y: 1, z: 2 };
match origin {
Point { y, .. } => println!("y is {}", y), // y is 1
}
}
if let
fn main() {
let some_u8_value = Some(0u8);
if let Some(3) = some_u8_value {
println!("three");
}
// shortcut for
match some_u8_value {
Some(3) => println!("three"),
_ => {}
}
}
Error handling
Types of errors
- errors that can be handled
enum Result<T, E> {
Ok(T),
Err(E),
}
- errors after which there's no use in running the program any further
panic!("unrecoverable error")
Opening file
use std::fs::File;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => panic!("Problem opening the file: {:?}", error),
};
}
Handling specific errors
use std::fs::File;
use std::io::ErrorKind;
fn main() {
let f = File::open("hello.txt");
let f = match f {
Ok(file) => file,
Err(error) => match error.kind() {
ErrorKind::NotFound => match File::create("hello.txt") {
Ok(fc) => fc,
Err(e) => panic!("Problem creating the file: {:?}", e),
},
other_error => {
panic!("Problem opening the file: {:?}", other_error)
}
},
};
}
Panic immediately
use std::fs::File;
fn main() {
// use unwrap when prototyping, when you are SURE the file exists
// or as a shortcut for:
// let f = match f {
// Ok(file) => file,
// Err(error) => panic!(),
// };
let f = File::open("hello.txt").unwrap();
// with message
let f = File::open("hello.txt")
.expect("Problem opening the file hello.txt");
}
Propagate error to calling function
use std::fs::File;
use std::io;
use std::io::{Read, Error};
fn main() {
let result = read_username_from_file();
match result {
Ok(username) => println!("username: {}", username),
Err(_) => println!("error reading username"),
}
}
fn read_username_from_file() -> Result<String, io::Error> {
let mut s = String::new();
File::open("username.txt")?.read_to_string(&mut s)?;
Ok(s)
}
Vectors
fn main() {
let v = vec![1, 2, 3];
// or
let mut v = Vec::new();
v.push(5);
v.push(6);
v.push(7);
v.push(8);
}
Get elements of vector
fn main() {
let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2]; // panicks when out of bounds
println!("The third element is {}", third);
match v.get(2) {
Some(third) => println!("The third element is {}", third),
None => println!("There is no third element."),
}
}
Iterating over vector
fn main() {
let v = vec![100, 32, 57];
for i in &v {
println!("{}", i);
}
// same as
let mut iter = IntoIterator::into_iter(v);
// or
// let mut iter = v.iter();
loop {
match iter.next() {
Some(i) => {
println!("{}", i);
},
None => break,
}
}
}
Mutating vector when iterating
fn main() {
let mut v = vec![100, 32, 57];
for i in &mut v {
*i += 50;
}
}
Exercise
Task
Create a turn based game applying the concepts from this post.
Solution
Check out my learning Rust repo on GitHub!
Top comments (0)