Rust Tutorial

Rust TutorialI have received numerous requests to cover the Rust programming language because there is very little information on it and really no books. So, in this tutorial I’ll provide an introduction to the Rust programming language. We’ll be covering Primitives, Output, Math Functions, Conditionals, Looping, Strings, Input, Arrays, Vectors, Tuples, Functions, Closures, Pointers, Structs, Traits, Enums and a whole lot more.

All of the code and a transcript of the video follows below.

If you like videos like this, consider supporting me with a $1 on Patreon.

[googleplusone]

Code From the Video

/*
Install Rust on Mac by typing the following in a terminal
curl -sSf https://static.rust-lang.org/rustup.sh | sh

Install using an installer for Windows
https://www.rust-lang.org/downloads.html

Type the following to verify install rustc --version

Install Rust support in Atom
Packages -> Command Palette -> Toggle
Type install -> Install Packages and Themes
atom-language-rust -> Click Install

Compile with rustc rusttut.rs -A warnings
Run ./rusttut or rusttut.exe on Windows
*/

// Import data types for testing
use std::{i8, i16, i32, i64, u8, u16, u32, u64, isize, usize, f32, f64};

use std::io::stdin;

// The main function executes when you run the program
fn main() {

    // This macro prints to the screen
    println!("Hello World");

    // let defines a variable
    // The data type will be guessed if not provided
    // Variable values are immutable (Can't change)
    let num = 10;

    // Define a 32 bit mutable integer
    let mut age: i32 = 40;

    // There are many number types i8, i16, i32,
    // i64, u8, u16, u32, u64, isize, usize, f32, f64

    println!("Max i8 {}", i8::MAX);
    println!("Min i8 {}", i8::MIN);
    println!("Max i16 {}", i16::MAX);
    println!("Min i16 {}", i16::MIN);
    println!("Max i32 {}", i32::MAX);
    println!("Min i32 {}", i32::MIN);
    println!("Max i64 {}", i64::MAX);
    println!("Min i64 {}", i64::MIN);
    println!("Max isize {}", isize::MAX);
    println!("Min isize {}", isize::MIN);
    println!("Max usize {}", usize::MAX);
    println!("Min usize {}", usize::MIN);
    println!("Max f32 {}", f32::MAX);
    println!("Min f32 {}", f32::MIN);
    println!("Max f64 {}", f64::MAX);
    println!("Min f64 {}", f64::MIN);

    // There are booleans
    let is_it_true: bool = true;

    // Characters
    let let_x: char = 'x';

    // Place variable values in output
    println!("I am {} years old", age);

    // You can define multiple variables
    let (f_name, l_name) = ("Derek", "Banas");

    // ---------- OUTPUT ----------

    // You can place data multiple times
    println!("It is {0} that {1} is {0}",
    is_it_true, let_x);

    // Format output
    println!("{:.2}", 1.234);
    println!("B: {:b} H: {:x} O: {:o}", 10, 10, 10);

    // Use named arguments
    // Define whitespace before data
    println!("{ten:>ws$}", ten=10, ws=5);

    // Pad output with zeros
    println!("{ten:>0ws$}", ten=10, ws=5);

    // ---------- MATH ----------
    println!("5 + 4 = {}", 5 + 4);
    println!("5 - 4 = {}", 5 - 4);
    println!("5 * 4 = {}", 5 * 4);
    println!("5 / 4 = {}", 5 / 4);
    println!("5 % 4 = {}", 5 % 4);

    let mut neg_4 = -4i32;

    println!("abs(-4) = {}", neg_4.abs());
    println!("4 ^ 6 = {}", 4i32.pow(6));
    println!("sqrt 9 = {}", 9f64.sqrt());
    println!("cbrt 9 = {}", 27f64.cbrt());
    println!("Round 1.45 = {}", 1.45f64.round());
    println!("Floor 1.45 = {}", 1.45f64.floor());
    println!("Ceiling 1.45 = {}", 1.45f64.ceil());
    println!("e ^ 2 = {}", 2f64.exp());
    println!("log(2) = {}", 2f64.ln());
    println!("log10(2) = {}", 2f64.log10());
    println!("90 to Radians = {}", 90f64.to_radians());
    println!("PI to Degrees = {}", 3.14f64.to_degrees());
    println!("Max 4, 5 = {}", 4f64.max(5f64));
    println!("Min 4, 5 = {}", 4f64.min(5f64));

    // sin, cos, tan, asin, acos, atan, atan2, sinh,
    // cosh, tanh
    println!("Sin 3.14 = {}", 3.14f64.sin());

    // ---------- CONDITIONALS ----------
    //

    let age_old = 6;

    if (age_old == 5) {
        println!("Go to kindergarten");
    } else if (age_old > 5) && (age_old <= 18){
        println!("Go to grade {}", (age_old - 5));
    } else if (age_old <= 25) && (age_old > 18) {
        println!("Go to college");
    } else {
        println!("Do what you want");
    }

    println!("!true = {}", !true);
    println!("true || false = {}", true || false);
    println!("true != false : {}", (true != false));

    // Ternary operator
    let can_vote = if (age_old >= 18) {true} else {false};
    println!("Can Vote : {}", can_vote);

    // ---------- LOOPING ----------
    let mut x = 1;

    loop {
        // If even print number
        if((x % 2) == 0) {
            println!("{}", x);
            x += 1;

            // Jump back to the beginning of the loop
            continue;
        }
        if(x > 10){

            // Jump out of the loop
            break;
        }
        x += 1;
        continue;

    }

    let mut y = 1;

    while y <= 10 {
        println!("WHILE : {}", y);
        y += 1;
    }

    for z in 1..10 {
        println!("FOR : {}", z);
    }

    // ---------- STRINGS ----------
    let rand_string = "I am a random string";

    // String length
    println!("Length : {}", rand_string.len());

    // Split a string in half at index
    let (first, second) = rand_string.split_at(6);
    println!("First : {} Second : {}", first, second);

    // Return an iterator for the string
    let count = rand_string.chars().count();
    let mut chars = rand_string.chars();

    let mut indiv_char = chars.next();

    loop {
        // Pattern match like switch
        match indiv_char {

            // If show print
            Some(x) => println!("{}", x),

            // If None break
            None => break,
        }
        indiv_char = chars.next();
    }

    // Split on whitespace
    let mut iter = rand_string.split_whitespace();

    let mut indiv_word = iter.next();

    loop {
        match indiv_word {
            Some(x) => println!("{}", x),
            None => break,
        }
        indiv_word = iter.next();
    }

    // Iterate over lines of string
    let rand_string2 = "I am a random string\nThere are other strings like it\nThis string is the best";

    let mut lines = rand_string2.lines();

    let mut indiv_line = lines.next();

    loop {
        match indiv_line {
            Some(x) => println!("{}", x),
            None => break,
        }
        indiv_line = lines.next();
    }

    // Find string in string
    println!("Find Best : {}", rand_string2.contains("best"));


    // ---------- INPUT ----------
    // Define the name for the outer loop
    'outer: loop {

        // Define our lucky number
        let number: i32 = 10;
        println!("Pick a Number");

        loop {

            // Create our string
            let mut line = String::new();

            // Pass the reference where we store the string
            // entered on the keyboard
            let input = stdin().read_line(&mut line);

            // An Option value is either Some with a value or None
            // ok() means that the reader is at the end of the line
            // map_or() applies a default value, or
            // applies functions to the value
            // trim() removes the newline
            // parse converts the string to a i32
            let guess: Option<i32> = input.ok().map_or(None, |_| line.trim().parse().ok());

            match guess {
                None => println!("Enter a Number"),
                Some(n) if n == number => {
                    println!("You Guessed It");
                    break 'outer;
                }
                Some(n) if n < number => println!("Too Low"),
                Some(n) if n > number => println!("Too High"),
                Some(_) => println!("Error")
            }
        }
    }

    // ---------- ARRAYS ----------
    // Arrays are fixed sized lists of the same type
    let rand_array = [1,2,3];

    // Get array by index
    println!("{}", rand_array[0]);

    // Get array length
    println!("{}", rand_array.len());

    // Slice an array by using a reference to it
    // :? formats the printing of the array
    println!("Second 2 : {:?}", &rand_array[1..3]);

    // ---------- VECTORS ----------
    // Vectors can grow unlike arrays

    let mut vect1 = vec![1,2,3,4,5];

    // Get an index
    println!("Item 2 : {}", vect1[1]);

    // Iterate through Vectors
    for i in &vect1 {
        println!("Vect : {}", i);
    }

    // Push item on
    vect1.push(6);

    // Pop item off
    vect1.pop();

    // ---------- TUPLES ----------
    // Tuples are fixed sized lists of many types
    let rand_tuple = ("Derek", 40);

    // You can also define the data types
    let rand_tuple_2: (&str, i8) = ("Derek", 40);

    // Get value by index
    println!("Name : {}", rand_tuple_2.0);

    // ---------- FUNCTIONS ----------
    say_hello("Derek");

    println!("5 + 4 = {}", get_sum(5,4));

    // We can create a binding to a function
    let sum = get_sum;
    println!("6 + 4 = {}", sum(6,4));

    // ---------- CLOSURES ----------
    // Closures represent blocks of code and can
    // except parameters and be passed to functions

    let sum_nums = |x: i32, y: i32| x + y;
    println!("7 + 8 = {}", sum_nums(7,8));

    // We can access variables outside the closure
    let num_ten = 10;
    let add_10 = |x: i32| x + num_ten;
    println!("5 + 10 = {}", add_10(5));

    // ---------- OWNERSHIP / POINTERS ----------
    // There is only one binding for each resource
    // so if you assign data to another variable
    // the original can't access the data
    let vect1 = vec![1, 2, 3];
    let vect2 = vect1;
    // error: use of moved value: `vect1`
    // println!("vect1[0] : {}", vect1[0]);

    // Primitive types can however copy values
    let prim_val = 1;
    let prim_val2 = prim_val;
    println!("prim_val : {}", prim_val);

    // Throws an error because you can't copy
    // the vector
    // println!("Sum of Vect : {}", sum_vects(vect2));
    // println!("Vect : {:?}", vect2);

    // If we pass a reference we avoid the error
    println!("Sum of Vect : {}", sum_vects(&vect2));
    println!("Vect : {:?}", vect2);

    // ---------- STRUCTS ----------
    // Create a mutable circle
    let mut circle1 = Circle {
        x: 10.0, y: 10.0, radius: 10.0};

    // Get Circle values
    println!("X : {} Y : {} R : {}",
     circle1.x, circle1.y, circle1.radius);

     // Define a function to operate on the struct
     println!("Circle Radius : {}", get_radius(&circle1));

     // It is recommended to create struct methods with impl
     println!("Circle X : {}", circle1.get_x());

     // ---------- TRAITS ----------
     // Defines functionality that a type provides

     println!("Circle Area : {}", circle1.area());

     let mut rect1 = Rectangle {
         height: 10.0, width: 10.0};

     println!("Rect Area : {}", rect1.area());


     // ---------- ENUMS ----------

     let hulk = Hero::Strong(100);
     let quicksilver = Hero::Fast;

     // to_owned() converts a string literal into
     // a String
     let spiderman = Hero::Info {name: "Spiderman".to_owned(),
        secret: "Peter Parker".to_owned()};

    get_info(hulk);
    get_info(spiderman);

} // ---------- END OF MAIN ----------

// ---------- STRUCTS ----------
// Structs are used to create custom data types
// Let's define a circle
struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

fn get_radius(circle: &Circle) -> f64 {
    circle.radius
}

// It is recommended to define struct methods with impl
impl Circle {
    pub fn get_x(&self) -> f64 {
        self.x
    }
}

// ---------- TRAITS ----------
// Defines functionality that a type provides
// If we want to access the y value from 2 Structs
// we could create 2 functions or create a trait

struct Rectangle {
    height: f64,
    width: f64,
}

// Define the trait which is like an interface
trait HasArea {
    fn area(&self) -> f64;
}

// Now we can implement the HasY interface for both structs
impl HasArea for Circle {
    fn area(&self) -> f64 {
        3.14159 * (self.radius * self.radius)
    }
}

impl HasArea for Rectangle {
    fn area(&self) -> f64 {
        self.height * self.width
    }
}

// ---------- ENUMS ----------
// An enum can have 1 of several values
enum Hero {
    Fast,
    Strong(i32),
    Info {name: String, secret: String}
}

// Receives enum
fn get_info(h: Hero){
    match h {
        Hero::Fast => println!("Fast"),
        Hero::Strong(i) => println!("Lifts {} tons", i),
        Hero::Info {name, secret} => {
            println!("{} is {}", name, secret);
        },
    }
}

// ---------- FUNCTIONS ----------
// You define functions with fn, attributes and
// attribute data types
// & means we are borrowing the value passed
fn say_hello(name: &str){
    println!("Hello {}", name);
}

// Receives 2 values and returns 1
fn get_sum(num1: i32, num2: i32) -> i32 {

    // Value returned
    // You can also use return VALUE;
    num1 + num2
}

// ---------- OWNERSHIP / POINTERS ----------

fn sum_vects(v1: &Vec<i32>) -> i32 {

    // Fold is a iterator adapter that applies
    // a function to all values
    // Takes initial value and a closure which
    // receives an accumulator and an element
    let sum = v1.iter().fold(0,
        |mut sum, &x| {sum += x; sum});

    return sum;
}

Leave a Reply

Your email address will not be published. Required fields are marked *