Overview

What is Rust

  • A statically complied language use LLVM as backend.
  • supports many platform and architectures.
  • used for a wide of range devices.

Why Rust

  • Compile time memory safety.
    • No uninitialized variables
    • Mostly no memory leaks.
    • No use-after-free and double frees.
    • No NULL pointer.
    • No forgotten locked mutex.
    • No data races between threads.
    • NO iterator invalidation.
  • No undefined behavior at runtime.
    • Array access is bounds checked.
    • Integer overflow is defined.
  • Modern Features
    • Enums and pattern searching
    • Generics
    • No overhead FFI(Foreign Language Interface)
    • Useful dev tools
      • Great compile errors.
      • Built-in dependency manager.
      • Built-in support for testing.

Common Programming Concepts

Variables and Mutability

Variables are immutable by default, make them mutable by adding mut in front of the variable name.

fn main() {
	let mut x = 5;
	x = 6;
	println!("x: {x}");
}

Constants

Constants are immutable, but there are a few differences between constants and immutable variables.

  1. mut is not allowed to add in front of constants, constants are immutable always.
  2. declare constants with const instead of let.
  3. constants must be annotated.
  4. can be declared in any scope, including the global scope.
  5. constants must set only to a constant expression, not a value computed in runtime.
// naming convention: all uppercase with underscores between two words.
const TWO_HOURS_IN_SECONDS: u32 = 2 * 60 * 60;

Shadowing

You can declare a new variable with the same name as a previous variable, the second variable overshadows the first.

fn main() {
	let y = 1:
	let y = y + 1;
	{
		let y = y * 2;
		println!("inner scope y: {}");
	}
	println!("y: {y}");
}

differences between shadowing and making a variable as mut:

  1. get a compile-time error when resigning the variable without let.
  2. shadowing can change variable type.
let space = "    ";
let space = space.len();

Data Types

Rust is statically typed language, every value in rust is of a certain data type, there are two data type subsets: scalar and compound.

Scalar Types

A scalar type represents a single value. Rust has four primary scalar types:

  • integers
    • i8/16/32/64
    • u8/16/32/64
    • isize/usize
    • i32 is the default type
    • use _ as a visual separator, 10_222
    • allow a type suffix, 42u8
  • floating-point numbers
    • f32/64
    • f64 is the default type
  • booleans
    • true/false
    • 1 byte in size
  • characters
    • char
    • specify char literals with ''
    • specify string literals with ""
    • 4 byte in size
    • represent a Unicode Scalar Value

Compound Types

  • tuple
    • (1, 2.0, 3, 'a')
  • array
    • [1, 2, 3]
    let a: [i32; 5] = [1, 2, 3, 4, 5]
    let a = [3; 5]
    
    • invalid array element access raise an runtime error

Functions

fn main() { // program entry
	println!("Hello, world!");

	another_function(5);

	let x - five();
	println!("The value of x is: {x}")
}

fn another_function(x: i32) {
	println!("The value of x is: {x}");
}

fn five() -> i32 {
	5
}
  • main is the entry point of program
  • fn define new functions
  • snake case as the conventional style for function and variable names
  • parameters are special variables that are part of a function’s signature, arguments are concrete values for these parameters
  • type of each parameter is required in function signature.
  • function bodies are made up of a series of statements optionally ending in an expression.
  • statements are instructions that perform some action and do not return a value, so you can’t assign a let statement to another variable.
  • expressions evaluate to a resultant value, calling a function/macro, a new scope block created with curly brackets are all expressions.
  • in a new scope block, if you add a semicolon to the end of an expression, you turn it into a statement.
  • expressions can be part of statements.

Control Flow

  • if
    • condition must be a bool
    • use match if too many cases
    • if is an expression, so it can be on the right side of a let statement.
  • loop
    • you can return values from loops
    • loop labels to disambiguate between multiple loops, which begins with a single quote
  • while
    • used for conditional loop
  • for
    • used for looping through collections
    fn main() {
    	let a = [1, 2, 3]
    	for v in a {
    		println!("the value is: {v}")
    	}
    }
    
    • Range like (1..4)

Ownership System

Ownership is a set of rules that govern how a Rust program manages memory. programs have to manage the way they use a computer’s memory while running.

  • garbage collection
  • programmer explicitly allocate and free the memory
  • Rust: memory is managed through a system of ownership with a set of rules that the compiler checks

Stack and Heap

Both the stack and the heap are parts of memory available to your code to use at runtime. The stack stores values in the order it gets them and removes the values in the opposite order, which is referred to as last in, first out. Two ways of manipulating data in stack:

  • Pushing onto the stack
  • Popping off the stack All data stored on the stack must have a known, fixed size. The heap is less organized: when you put data on the heap, you request a certain amount of space. The memory allocator finds an empty spot in the heap marks it as being in use, and returns a pointer, which is the address of that location. The process is called allocating on the heap or allocating. The pointer is a known, fixed size, which can be stored on the stack.

Pushing onto the stack is faster than allocating on the heap because the allocator never has to search for a place to store new data; the location is always at the stack.

Allocating space on the heap requires more work because the allocator must find a big enough space and then perform bookkeeping to prepare for the next allocation.

When calling a function, the values passed into the function(including, pointers) and the function’s local variables get pushed onto the stack, and get popped off the stack when over.

Ownership Rules

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