InstallationEdit

Windows InstallEdit

Download and install[1]Edit

https://static.rust-lang.org/rustup/dist/x86_64-pc-windows-msvc/rustup-init.exe

Link.exe fixEdit

https://visualstudio.microsoft.com/visual-cpp-build-tools/ and it will download a Visual Studio Installer.

Run it as Administrator, then make sure you download all three things listed below in the screenshot, versions don't matter, just try get latest ones. Imagerust.png Hit Install. May need to reboot computer. Definitely need a new shell to test.

Linux installEdit

curl https://sh.rustup.rs -sSf | sh

CodeEdit

Using statementsEdit

use std::io;
use rand::Rng;
use std::cmp::Ordering;

Function definitionEdit

fn main() { }

Write to consoleEdit

println!("Guess the number!");

Write to console with variable string interpolationEdit

println!("The secret number is: {secret_number}");

Define immutable string (defaults to immutable)Edit

let x = 5;

Define mutable stringEdit

let mut guess = String::new();

Define constantEdit

const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

Read from console with error handlingEdit

io::stdin()
    .read_line(&mut guess)
    .expect("Failed to read line");

Cast string to unsigned 32 bit integer with error handlingEdit

let guess: u32 = guess.trim().parse().expect("Please type a number!");

Variable comparison and case optionsEdit

match guess.cmp(&secret_number) {
    Ordering::Less => println!("Too small!"),
    Ordering::Greater => println!("Too big!"),
    Ordering::Equal => println!("You win!"),
}

Get random number with rangeEdit

let secret_number = rand::thread_rng().gen_range(1..=100);

Validating inputEdit

let guess: u32 = match guess.trim().parse() {
    Ok(num) => num,
    Err(_) => continue,
};

Variable shadowingEdit

fn main() {
    let x = 5;
    let x = x + 1;
    {
        let x = x * 2;
        println!("The value of x in the inner scope is: {x}");
        println!("The value of x is: {x}");
    }
    println!("The value of x is: {x}");
}

Variable is declared as 5. This is overshadowed by its redefinition to 6. It is shadowed again by the inner block where its value is 12, but this shadowing is only in scope for the inner block. Once this goes out of scope, it returns back to 6. Shadowing can be used to redefine a type also (i.e. name reuse) if it is mutable.

let spaces = "    ";
let spaces = spaces.len();

Variable typesEdit

IntegersEdit

Length Signed Unsigned Values
8-bit i8 u8 256
16-bit i16 u16 65536
32-bit i32 u32 4.29E+09
64-bit i64 u64 1.84E+19
128-bit i128 u128 3.4E+38
arch isize usize

Arch is the architecture type. You can write integer literals in any of the forms shown in Table 3-2. Note that number literals that can be multiple numeric types allow a type suffix, such as 57u8 , to designatetype. Number literals can also use _ as a visual separator to make the number easier to read, such as 1_000 , which will have the same value as if you had specified 1000 .

Number literals Example
Decimal 98_222
Hex 0xff
Octal 0o77
Binary 0b1111_0000
Byte ( u8 only) b'A'

Floating pointEdit

Rust's floating-point types are f32 and f64.

fn main() {
    let x = 2.0; // f64
    let y: f32 = 3.0; // f32
}

Floating-point numbers are represented according to the IEEE-754 standard. The f32 type is a single-precision float, and f64 has double precision.

Arithmetic operationsEdit

fn main() {
    // addition
    let sum = 5 + 10;
    // 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
    let remainder = 43 % 5;
}

BooleanEdit

fn main() {
    let t = true;
    let f: bool = false; // with explicit type annotation
}

CharEdit

fn main() {
    let c = 'z';
    let z: char = 'Z'; // with explicit type annotation
    let heart_eyed_cat = '��';
}

TuplesEdit

fn main() {
    let tup: (i32, f64, u8) = (500, 6.4, 1);
}
Tuple with deconstructionEdit
fn main() {
    let tup = (500, 6.4, 1);
    let (x, y, z) = tup;
    println!("The value of y is: {y}");
}

Tuple accessing

fn main() {
    let x: (i32, f64, u8) = (500, 6.4, 1);
    let five_hundred = x.0;
    let six_point_four = x.1;
    let one = x.2;
}

ArrayEdit

fn main() {
    let a = [1, 2, 3, 4, 5];
}
let months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
let a: [i32; 5] = [1, 2, 3, 4, 5];

Initialize an array to contain the same value for each element by specifying the initial value, followed by a semicolon, and then the length of the array in square brackets:

let a = [3; 5];

Array accessEdit

fn main() {
    let a = [1, 2, 3, 4, 5];
    let first = a[0];
    let second = a[1];
}

Functions with arguments / parametersEdit

fn another_function(x: i32) {
    println!("The value of x is: {x}");
}
fn main() {
    print_beled_measurement(5, 'h');
}
fn print_labeled_measurement(value: i32, unit_label: char) {
    println!("The measurement is: {value}{unit_label}");
}

Statements and ExpressionsEdit

• Statements are instructions that perform some action and do not return a value.

• Expressions evaluate to a resultant value.

fn main() {
    let y = {
        let x = 3;
        x + 1
    };
}
println!("The value of y is: {y}");

Functions with return and argumentsEdit

fn main() {
    let x = plus_one(5);
    println!("The value of x is: {x}");
}

fn plus_one(x: i32) -> i32 {
    x + 1
}

Note - notice that the last line above does not end with a semicolon. Adding one will cause a problem.

CommentsEdit

// this is a comment

Control flowEdit

ifEdit

fn main() {
    let number = 3;
    if number != 0 {
        println!("number was something other than zero");
    }
}
fn main() {
    let number = 6;
    if number % 4 == 0 {
        println!("number is divisible by 4");
    } else if number % 3 == 0 {
        println!("number is divisible by 3");
    } else if number % 2 == 0 {
        println!("number is divisible by 2");
    } else {
        println!("number is not divisible by 4, 3, or 2");
    }
}
let number = if condition { 5 } else { 6 };

loops and breakingEdit

loop{
    match guess.cmp(&secret_number) {
        Ordering::Less => println!("Too small!"),
        Ordering::Greater => println!("Too big!"),
        Ordering::Equal => {
            println!("You win!");
            break;
        }
    }
}

Breaking out of nested loops

fn main() {
    let mut count = 0;
    'counting_up: loop {
        println!("count = {count}");
        let mut remaining = 10;
        loop {
            println!("remaining = {remaining}");
            if remaining == 9 {
                break;
            }
            if count == 2 {
                break 'counting_up;
            }
            remaining -= 1;
        }
        count += 1;
    }
    println!("End count = {count}");
}
fn main() {
    let mut counter = 0;
    let result = loop {
        counter += 1;
        if counter == 10 {
            break counter * 2;
        }
    };
    println!("The result is {result}");
}

whileEdit

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

forEdit

fn main() {
    let a = [10, 20, 30, 40, 50];
    for element in a {
        println!("the value is: {element}");
    }
}

fn main() {
    for number in (1..4).rev() {
        println!("{number}!");
    }
    println!("LIFTOFF!!!");
}

StackEdit

Both the stack and the heap are parts of memory but they are structured in different ways. The stack stores values in the order it gets them and removes the values in the opposite order. last in, first out. Adding data is called pushing and removing data is called popping

All data stored on the stack must have a known, fixed size.

HeapEdit

Data is stored on the heap by requesting a certain amount of space. The memory allocator finds an empty spot in the heap that is big enough, marks it as being in use, and returns a pointer, which is the address of that location. This process is called allocating on the heap. Because the pointer to the heap is a known, fixed size, the pointer can be stored on the stack, but to access the actual data, the pointer must be followed.

Pushing / popping the stack is faster than allocating on the heap.

Ownership RulesEdit

Ownership rules:

  • Each value in Rust has an owner
  • There can only be one owner at a time.
  • When the owner goes out of scope, the value will be dropped.

Variable scopeEdit

StringsEdit
let s1 = String::from("hello");
let s2 = s1;
println!("{}, world!", s1);

The string "hello" is placed on the heap and the metadata i.e. pointer is stored on the stack. When the second line is executed, the stack data is changed and s2 becomes the owner of the string so s1 becomes invalid. This is like a shallow copy (like a 'by ref') but because of the invalidation it is known as a move. A deep copy, where the heap actual "hello" is duplicated also, use clone.

let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);

Objects stored on the stack can be copied without the above detail where clone and shallow copy are identical

let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);

• All the integer types

• The Boolean type, bool

• All the floating-point types

• The character type, char

• Tuples, if they only contain types that also implement Copy, eg (i32, i32) implements Copy

fn main() {
	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
	let x = 5;			 			// x comes into scope
	makes_copy(x);					// x would move into the function,
									// but i32 is Copy, so it's okay to still
									// use x afterward
} 									// Here, x goes out of scope, then s. But because s's value was moved, nothing special happens.

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.

fn makes_copy(some_integer: i32) { 			// some_integer comes into scope
	println!("{}", some_integer);
} 											// Here, some_integer goes out of scope. Nothing special happens.