triston-notes/Cards/dev/Rust Udemy Notes.md
2023-10-21 18:52:54 -05:00

7.4 KiB

up:: Rust tags::

Rust Notes

Rust is a modern systems programming language with:

  • memory safety (no garbage collection)
  • no null values
  • no exceptions
  • modern package manager and build system (similar to npm)
  • no data races (race conditions)

Installing rust

on macos/linux

after running command follow onscreen instructions

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

to confirm installation success run the following:

-> rustc --version
rustc 1.67.1 (d5a82bbd2 2023-02-07)

-> cargo --version
cargo 1.67.1 (8ecd4f20a 2023-01-10)

-> rustup --version
rustup 1.25.2 (17db695f1 2023-02-01)

in VSCode

install rust-analyzer extension


Exploring Cargo

By running cargo new <name> you will create a new rust project containing a git repository Calling cargo new --help will bring up help view

the Cargo.toml file is always at the root of a rust project. Its essentially the package.json in npm The dependency section contains whats called Crates. The central registry for all crates is found at Crates.io.

after a crate is installed, youll see the name of the crate, the version, and a thumb meaning its isntalled

[dependencies]
package = 1.0.0 👍

Building project

To build the cargo project you run cargo build. This will build all crates and your rust code.

Running project

To run the cargo project you run cargo run which will execute the binary file that was previously built

System wide crates

We can also use cargo to install rust binaries that can be used system wide. A tool called cargo-expand can be installed by running cargo install cargo-expand in the terminal

this takes a minute

cargo-expand is using the nightly build of cargo/rust so you cannot call it in the terminal yet

lets check what we have installed by running rustup toolchain list. Looks like we're on the stable version

lets install the nightly toolchain using the following command

rustup toolchain install nightly-aarch64-apple-darwin

Now if we run rustup toolchain list, we'll see the nightly and the stable with stable being default - we can now call cargo expand in the terminal


The stack

Rust calculates the size of a stack frame at compile time This means, if a function contains a variable, the stack frame will have just enough space for that variable. If a new frame is added with a parameter and a variable, the stack frame will have just enough space for the two variable, slightly larger than the previous stack frame

The stack has a limited size, determined by the machine architecture, and will result in a stack overflow error if you overflow the stack with something like infinite recursion


The Heap

What is the heap?
  • its a region fo the process emmory that is NOT auto managed
  • it has no size restrictions
  • its accessible by any function, anywhere in the program
  • we should avoid heap allocations as they are expensive

When you allocate a value on the heap, you store a pointer to the address of the allocated data

POINTER e = ALLOCATE INTEGER 7
// e = 0xf578bb60

memory is manually allocated and deallocate on the heap. If you dont deallocate the memory, you will have a memory leak, which wont be released until the program exits


Smart Pointers

smart pointers essnetially just deallocate the heap memory automatically

let e = Box::new(7);

Memory layout of binary

who cares


Basic data types

u8 - unsigned 8 bit (can only be positive) i8 - signed 8 bit (can be negative) u/i + size (8,16,32,128)

bool - 0/1 usize, isize - positive/negative size

char - single character (always 4 bytes)


Functions

the main() function is the entry point to any rust application function names are written in snake case i.e. some_function_name() annotate all parameters annotate return type

fn some_func(arg: f32) -> f32 {
	// return f32 type
	// if you dont use return keyword and without semicolon, value will be implicitly returned
}

We may see functions with a BANG attached to it, like so someFunc!(). That BANG indicates that this function is a MACRO.. A macro essentially is a shortcut to template code. So when we call the macro function, it will expand the code on build to something like so:

someFunc!('some arg');
// expands to 
bunc::of::junk -> stuff -> no idea what im writing{
	match(junk){
		no idea -> 'some arg'
	}
}

// this is just an example, this is nothing like the actual syntax of rust

Variables

we use the let keyword to define variables

let newVar = 5;

There's no need to define variable types as the rust compiler can usually infer the type

All variables in rust are immutable by default. If we try to update the variable, the compiler will cry about it.

let a = 'b';
a = 'c' // compiler error: variable immutable

let mut a = 'b';
a = 'c';
// a == 'c'

Standard Library

STD Module I/O (std::io)

// need to allocate empty mutable string first
let mut value = String::new(); // value lives on heap since size is not known at compile time - String is type of smart pointer
io::stdin().read_line(&mut value);

Ownership

there are a few rules that variables follow:

  1. each value in rust is owned by a variable
  2. when the owner goes out of scope, the value will be deallocated let mut input = String::new() where input is owner of String
  3. there can only be ONE owner at a time (let mut input = String::new() where input is owner of heap allocated String)

when passing owner to function, the function parameter now becomes the owner, and when that function is finished executing, you can no longer use the initial variable

let mut input = String::new(); // input is now owner
some_fn(input); // some_fn parameter is now owner
io::stdin().read_line(&mut input); // throws error as input is no longer available

if you dont like this, see Borrowing


Borrowing

by passing references, we can "borrow" a variable

fn some_fn(s: &string){} // s is not the owner of the string due to &

let val = String::new();
some_fn(&val) // reference is immutable

let mut val = String::new()
some_fn(&mut val) // reference is mutable

WITHIN a scope, you can have only one of:

  1. as many immutable references as you want
  2. one mutable reference
// cannot borrow 'input' as immutable because its already borrowed as mutable

Result Type

When a method returns a type Result, it returns essentially a javascript promise with a success (then) and an error (catch).

you can call .unwrap() on the result if result was a success, the unwrap will yield the contents of the success if result was an error, it will terminate the application


Parse string to number

Sometimes when getting input from the user for a number, you want to actually do some operation on that number.

io::stdin().read_line(&mut value).unwrap()

say we want to convert this value to an f32 - you would first need to ensure the string is trimmed

let new_value = value.trim();

then you can call parse on the string

new_value.parse::<f32>().unwrap();

the <f32> part is so parse an ensure what type we're wanting to convert it into


Resources

For more details on rust syntax, go to this file Rust Syntax overview


End

Now lets head on over to creating a http server Rust http server