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 ```Bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` to confirm installation success run the following: ```Bash -> 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 ` 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](crates.io). after a crate is installed, youll see the name of the crate, the version, and a thumb meaning its isntalled ```toml [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 ```bash 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 ```rust 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 ```rust 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 ```rust 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: ```rust 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 ```rust 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. ```rust let a = 'b'; a = 'c' // compiler error: variable immutable let mut a = 'b'; a = 'c'; // a == 'c' ``` --- ### Standard Library [STD](doc.rust-lang.org/std) Module I/O (std::io) ```rust // 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 ```rust 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 ```rust 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 ```rust // 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. ```rust 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 ```rust let new_value = value.trim(); ``` then you can call parse on the string ```rust new_value.parse::().unwrap(); ``` the `` 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]]