Busy Developer's Guide to Rust

ted.neward@newardassociates.com | Blog: http://blogs.newardassociates.com | Github: tedneward | LinkedIn: tedneward

Objectives

Get to know Rust

Overview

... of Rust

Overview

What is this thing?

Overview

What does all that mean?

Overview

What does all that mean?

Getting Started with Rust

From 0 to HelloWorld in 60 seconds

Getting Started with Rust

Install (on *nix-OS)

Getting Started with Rust

Install (package managers)

Getting Started with Rust

Verify

Getting Started with Rust

Hello, Rust!

Getting Started with Rust

Create project

Getting Started with Rust

main.rs

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

Getting Started with Rust

Cargo.toml

[package]
name = "hello"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at 
# https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]

Getting Started with Rust

Build and run

Getting Started with Rust

Slightly more complex example

Getting Started with Rust

Project dir

Getting Started with Rust

App vs lib

Getting Started with Rust

lib.rs

pub fn add(left: usize, right: usize) -> usize {
    left + right
}

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

    #[test]
    fn it_works() {
        let result = add(2, 2);
        assert_eq!(result, 4);
    }
}

Rust Basics

Getting the core down

Rust Basics

Start

Rust Basics

Basic syntax

Rust Basics

Basics

pub fn add(left: usize, right: usize) -> usize {
    left + right
}

Rust Basics

Scope blocks are expressions

Rust Basics

Comments

Rust Basics

Comments

// This is a standard comment
    
/// This is a documentation comment.
/// It supports Markdown syntax.
/// 
/// # Examples
///
/// ```
/// let arg = 5;
/// let answer = rust_basics::add(5, arg);
///
/// assert_eq!(10, answer);
/// ```
///
/// NOTE: rust_doc will sanity-check the code inside
/// the Examples part of these comments! 
/// NOTE: cargo test will test the code here too!

Rust Basics

Data types

Rust Basics

Scalar data types

Rust Basics

Scalars

pub fn math_on_scalars() {
    // addition
    let _sum = 5 + 10; // unused, so prefix with "_"
    
    // subtraction
    let _difference = 95.5 - 4.3;
    
    // multiplication
    let _product = 4 * 30;
    
    // division
    let _quotient = 56.7 / 32.2;
    let _truncated = -5 / 3; // Results in -1
    
    // remainder/modulo
    let _remainder = 43 % 5;
}

Rust Basics

Variable declarations

Rust Basics

Variables

pub fn immutable_x() -> i32 {
    let x = 5;
    //x = 6; // ERROR!
    println!("The value of x is: {x}");
    return x;
}
pub fn redeclaring_x() -> i32 {
    let x = 5;
    println!("The value of x is: {x}");
    let x = x + 1;
    println!("The value of x is: {x}");
    return x;
}
pub fn mutable_x() -> i32 {
    let mut x = 5;
    println!("The value of x is: {x}");
    x = 6;
    println!("The value of x is: {x}");
    return x;
}

Rust Basics

Constant declarations

Rust Basics

Compound types

Rust Control Flow

Making decisions, repeating stuff, ...

Rust Control Flow

If: boolean decision-making

Rust Control Flow

If

fn if_example() -> i32 {
    let test = 12;
    let mut result = -1;
    if test % 6 == 0 {
        result = 1;
    }
    else {
        result = 0;
    }
    // shorter version:
    let result = if test % 6 == 0 { 1 } else { 0 };
    result
    // or even shorter:
    //if test % 6 == 0 { 1 } else { 0 }
}

Rust Control Flow

Match: pattern-matching

Rust Control Flow

Match

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}
    
fn value_in_cents(coin: Coin) -> u8 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

Rust Control Flow

Match with Option type

fn might_return_nothing() -> Option<i32> {
    // do some complex randomization here
    // then always return 42
    Some(42)
}
fn get_something_from_nothing() -> i32{
    match might_return_nothing() {
        Some(x) => { 
            println!("We got {}", x);
            return x;
        },
        None => {
            println!("Nope, nada");
            return 0;
        }
    }
}

Rust Control Flow

Loop: unterminated looping

Rust Control Flow

Loop

fn loop_example() -> i32 {
    let mut counter = 0;
    
    let result = loop {
        println!("Looping on {counter}...");
        counter += 1;
    
        if counter == 10 {
            break counter * 2;
        }
    };
    
    return result;
}

Rust Control Flow

While: conditional looping

Rust Control Flow

Loop

fn while_countdown() {
    let mut number = 3;
    
    while number != 0 {
        println!("{number}!");
    
        number -= 1;
    };
    
    println!("LIFTOFF!!!");
}

Rust Control Flow

For: conditional looping

Rust Control Flow

Loop

fn for_examples() {
    let a = [10, 20, 30, 40, 50];
    
    for element in a {
        println!("the value is: {element}");
    }
    
    // Use a Range object, reversed
    for number in (1..4).rev() {
        println!("{number}!");
    }
    println!("LIFTOFF!!!");
}

Rust Functions

Declaring and defining blocks of code

Rust Functions

Basic syntax

Rust Functions

Function example

fn function_call() {
    let x = plus_one(5);
    
    println!("The value of x is: {x}");
}
    
fn plus_one(x: i32) -> i32 {
    x + 1           // NOTE: semicolon would make this a statement
                    // and therefore not an implicit return value
                    // and therefore an error!
    //return x + 1  // NOTE: acceptable
    //return x + 1; // NOTE: acceptable

Rust Functions

Expressions vs statements

Rust Functions

Closures/Anonymous functions

Rust Functions

Closure example

fn closure_call() {
    let print_int = |x: i32| println!("{}", x);
    print_int(5);
    
    let printing_add = |l: i32, r: i32| -> i32 {
        println!("Adding {} and {}.", l, r);
        l + r
          // This closure can be as long as we want, just like a function.
    };
    printing_add(5, 5);
}

Rust and O-O

How to do objects in Rust

Rust and O-O

Option 1: Don't

Rust and O-O

Procedural example

fn rectangle_area(width: u32, height: u32) -> u32 {
    width * height
}
    
fn use_prims() {
    let rect_w = 12;
    let rect_h = 24;
    let area = rectangle_area(rect_w, rect_h);
    println!("The area of a {rect_w} x {rect_h} rectangle is {area}");
}

Rust and O-O

Option 2: Tuples

Rust and O-O

Tuple example

fn rect_tuple_area(dimensions: (u32, u32)) -> u32 {
    dimensions.0 * dimensions.1
}
fn use_tuples() {
    let rect = (12, 24);
    let area = rect_tuple_area(rect);
    println!("The area of a {} x {} rectangle is {}", 
        rect.0, rect.1, area);
}

Rust and O-O

Option 3: Structs

Rust and O-O

Struct example

struct RectStruct {
    width: u32,
    height: u32,
}
    
fn rect_area(rect: &RectStruct) -> u32 {
    rect.width * rect.height
}
    
fn use_struct() {
    let rect = RectStruct{
        width: 12, 
        height: 24,
    };
    let area = rect_area(&rect);
    println!("The area of a {} x {} rectangle is {}", 
        rect.width, rect.height, area);
}

Rust and O-O

Option 4: Structs and Impls

Rust and O-O

Structs-and-Impls example

struct Rectangle { width: u32, height: u32, }
    
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
    fn new(h: u32, w: u32) -> Self {
        Self { height: h, width: w }
    }
    fn square(size: u32) -> Self {
        Self { height: size, width: size } 
    }
}
    
fn use_object() {
    //let rect = Rectangle{ width: 12, height: 24, };
    let rect = Rectangle::new(12, 24);
    println!("The area of a {} x {} rectangle is {}", 
        rect.width, rect.height, rect.area());
    let rect = Rectangle::square(12);
    println!("The area of a {} x {} square is {}", 
        rect.width, rect.height, rect.area());
}

Rust and O-O

Sidebar: Traits

Rust and O-O

Structs-and-Impls-and-Traits example

#[derive(Debug)] // this is an "attribute" using the Debug "trait"
struct DRectangle {
    width: u32,
    height: u32,
}
    
impl DRectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}
    
fn use_trait() {
    let rect = DRectangle{
        width: 12, 
        height: 24,
    };
    dbg!(&rect);    // "[src/main.rs:95:5] &rect = DRectangle {
                    //        width: 12,
                    //        height: 24,
                    //  }" printed to stderr
    println!("The area of a {} x {} rectangle is {}", 
        rect.width, rect.height, rect.area());
}

Ownership

Tracking memory semantics

Ownership

What is this?

Ownership

SIDEBAR: Stack vs heap

Ownership

SIDEBAR: Scope and scope rules

Ownership

Ownership & Scope

Ownership

Simple scope/lifecycle example

{
    let s = String::from("hello"); 
        // s is valid from this point forward

    println!("{} world", s);
}       // this scope is now over
        // s is no longer valid

Ownership

Stack/heap and scope

Ownership

Scope example

let x = 20;
{
    let y = 15;
    x = y;
} // What is x here? What is y here?

Ownership

So what should happen here?

{ 
    let s1 = String::from("hello");
    let s2 = s1;
    // ... more code ...
}

Ownership

Or here?

let s1 = String::from("hello"); 
{
    let s2 = String::from("world");
    s1 = s2;
}

Ownership

Ownership rules

Ownership

Stack ownership

Ownership

Heap ownership

Ownership

Ownership and function parameters

Ownership

Watch our string... disappear!

fn taking() {
    let s = String::from("hello");  // s comes into scope
    
    takes_ownership(s);             // s's value moves into the function...
                                    // ... and so is no longer valid here
    
    println!("{}", s);            // compile error!
}
    
fn takes_ownership(some_string: String) { // some_string comes into scope
    println!("{}", some_string);
} // Here, some_string goes out of scope and `drop` is called. The backing
  // memory is freed.

Ownership

Watch what Rust tells us

error[E0382]: borrow of moved value: `s`
  --> src/main.rs:7:20
   |
2  |     let s = String::from("hello");  // s comes into scope
   |         - move occurs because `s` has type `String`, which does not implement the `Copy` trait
3  |
4  |     takes_ownership(s);             // s's value moves into the function...
   |                     - value moved here
...
7  |     println!("{}", s);            // compile error!
   |                    ^ value borrowed here after move
   |
note: consider changing this parameter type in function `takes_ownership` to borrow instead if owning the value isn't necessary
  --> src/main.rs:10:33
   |
10 | fn takes_ownership(some_string: String) { // some_string comes into scope
   |    ---------------              ^^^^^^ this parameter takes ownership of the value
   |    |
   |    in this function
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider cloning the value if the performance cost is acceptable
   |
4  |     takes_ownership(s.clone());             // s's value moves into the function...
   |                      ++++++++

For more information about this error, try `rustc --explain E0382`.
error: could not compile `ownership` (bin "ownership") due to 1 previous error

Ownership

Ownership and return values

Ownership

Follow the ownership chain

fn takeing_and_giving() {
    let s1 = gives_ownership();         // move
    let s2 = String::from("hello");
    let s3 = takes_and_gives_back(s2);  // move into takes_and_gives_back, which also
                                        // moves its return value into s3
}
fn gives_ownership() -> String {
    let some_string = String::from("yours");
    some_string                         // move out to caller
}
fn takes_and_gives_back(a_string: String) -> String {
    a_string                            // moves out to caller
}

Ownership

This can get tedious

Ownership

Rust adds references

Ownership

Implementing that string-length function

fn references_basics() {
    let s = String::from("hello");
    let len = calculate_length(&s);        // notice the "&" in the call
    println!("The length of '{s1}' is {len}.");
}
    
fn calculate_length(s: &String) -> usize {  // notice the "&" in the declaration
    //s.push_str(" I am in you!");          // compile error!
    s.len()
}

Ownership

Rust also has mutable references

Ownership

Mutating the passed string

fn mutable_references_basics() {
    let mut s = String::from("hello");
    change(&mut s);
    println!("{}", s);
}
    
fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

Ownership

Laws of Rust References

Summary

Rust is....

Resources

Where to go for more

Resources

Books

Resources

Websites

Credentials

Who is this guy?