DEV Community

Cover image for Rust 2 - Memory and advanced structures
Petr Janik
Petr Janik

Posted on • Edited on

4 1

Rust 2 - Memory and advanced structures

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`
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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()
}
Enter fullscreen mode Exit fullscreen mode

Mutable reference

fn main() {
    let mut s = String::from("hello");

    change(&mut s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

...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);
Enter fullscreen mode Exit fullscreen mode

Slice

let s = String::from("hello world");

let hello = &s[0..5];  // hello
let world = &s[6..11]; // world
Enter fullscreen mode Exit fullscreen mode

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[..]
}
Enter fullscreen mode Exit fullscreen mode

Structures

Definition

struct User {
    username: String,
    email: String,
    sign_in_count: u64,
    active: bool,
}
Enter fullscreen mode Exit fullscreen mode

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");
}
Enter fullscreen mode Exit fullscreen mode

Shortcut for struct creation

fn build_user(email: String, username: String) -> User {
    User {
        email,
        username,
        active: true,
        sign_in_count: 1,
    }
}
Enter fullscreen mode Exit fullscreen mode

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
    };
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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));
}
Enter fullscreen mode Exit fullscreen mode

Enums

Simple enum

enum Delivery {
    Pickup,
    Parcel,
}

let delivery = Delivery::Pickup;
Enter fullscreen mode Exit fullscreen mode

Enums like tuple structs

enum Delivery {
    Pickup,
    Parcel(String),
}

let delivery = Delivery::Parcel(String::from("Queens, New York 11434"));
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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.");
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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
        ),
    }
}
Enter fullscreen mode Exit fullscreen mode

Multiple options

fn main() {
    let x = 1;

    match x {
        1 | 2 => println!("one or two"),
        3 => println!("three"),
        _ => println!("anything"),
    }
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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);
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

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
    }
}
Enter fullscreen mode Exit fullscreen mode

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"),
        _ => {}
    }
}
Enter fullscreen mode Exit fullscreen mode

Error handling

Types of errors

  • errors that can be handled
enum Result<T, E> {
    Ok(T),
    Err(E),
}
Enter fullscreen mode Exit fullscreen mode
  • errors after which there's no use in running the program any further
panic!("unrecoverable error")
Enter fullscreen mode Exit fullscreen mode

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),
    };
}
Enter fullscreen mode Exit fullscreen mode

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)
            }
        },
    };
}
Enter fullscreen mode Exit fullscreen mode

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");
}
Enter fullscreen mode Exit fullscreen mode

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)
}
Enter fullscreen mode Exit fullscreen mode

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);
}
Enter fullscreen mode Exit fullscreen mode

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."),
    }
}
Enter fullscreen mode Exit fullscreen mode

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,
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Mutating vector when iterating

fn main() {
    let mut v = vec![100, 32, 57];
    for i in &mut v {
        *i += 50;
    }
}
Enter fullscreen mode Exit fullscreen mode

Exercise

Task

Create a turn based game applying the concepts from this post.

Solution

Turn based game


Check out my learning Rust repo on GitHub!

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay