• Any function whose name ends with a ! is a macro. Macros and functions are different in how they operate. For example
println!(); //this is a macro
println();  //this is a function
  • Rust macros are executed at compile-time and are hygienic in compilation and execution
  • Rust is a statically-typed language
  • Rust does not have a garbage collector
  • If you see a .pdb filename extension, that is a debugging information file
  • Crates are packages of codes. I think of them as libraries that go under the [dependencies] section in cargo.toml
  • Source code files go into the src subdirectory. Everything else goes into the parent directory
  • When building a cargo directory, the executable file will be found in the default debug build folder (/target/debug/)

Note

When executing a .exe file in PowerShell, type ./exe_name

  • When accessing invalid or non-extant elements of an array, Rust will not execute the code and spit garbage values; it will stop compiling and will throw an error. This is a key part of Rust’s memory safety principles

  • let defines an immutable variable by default. To declare a mutable variable, use let mut

  • To declare a variable along with its type, use type annotations

let x: u32 = 69;

Difference between mut and shadowing

  • mut allows the variable’s contents to be changed but the type cannot be mutated. In shadowing, however, that is possible because a new variable is being created from scratch
  • Shadowing eliminates the difficulty of naming variables appropriate to the use case. This is not possible using mut

Statements and Expressions

  • A statement does not evaluate to a value. It just performs some process

    • let x = 5; is a statement. let x = (y = 6); will return an error because it’s a statement
  • An expression evaluates to a value

  • Expressions do not include ending semicolons. Including one turns an expression into a statement

  • Return statements are expressions and are not named

  • Conditions associated with if statements are called arms

  • Rust will not automatically convert non-Boolean types to bool, unlike Python

  • References are immutable by default

  • Libraries are called crates in Rust

  • Find crates and Rust open-source projects in https://crates.io/

Cargo.lock The Cargo.lock file is created to ensure reproducible builds. When you build a program with external dependencies (crates) for the first time after specifying the dependency details in Cargo.toml, it will download the dependencies that match the criteria for the project and use them. It then creates a Cargo.lock file with the version numbers of the dependencies it’s using. In the future, rebuilds will be fast because Rust can directly reference Cargo.lock

  • Cargo.lock is checked into source control easy for portable rebuilds

To convert one type to another, do this

let var_new = match var_old.trim().parse() {
	Ok(num) => num,
	Err(_) => continue,
}

match is much like switch in C++ and allows for strong pattern matching trim() removes all whitespaces and newlines. Useful for when you’re converting a string to a number, for example parse() does the type conversion Ok(num) => num is when the conversion is valid and the converted data is put in a num buffer (you don’t have to separately declare num). If there was an error, Err(_) will capture any error (_ is a catchall wildcard for any and all errors) and just continue code execution, skipping the conversion

Ownership

Source: Ownership - Rust Programming Book

  • [I] This could go into a different note - Why did I choose Rust?

  • Set of rules that govern how Rust uses memory

  • Rust compiler checks for certain ownership rules. If any of those rules are violated, the program won’t compile

  • Ownership is to manage heap memory

    • Heap memory is slower than stack allocation, and can be chaotic

Ownership rules

  • Each value has an owner
  • There can only be one owner at a time
  • When the owner goes out of scope, the value will be dropped
  • [!] Remember this!

String is a variable type that stores data in the heap. It can be mutated String literals cannot be mutated but String data can

When a variable goes out of scope, Rust automatically frees the memory it has occupied - 3rd ownership rule. The drop function is auto-called

  • [n] Resource Acquisition Is Initialization (RAII)

Variable Scope Exit Error

error[E0382]: borrow of moved value … — move occurs because … which does not implement the ‘Copy’ trait … = note: this error originates in the macro $crate::format_args_nl which comes from the expansion of the macro println (in Nightly builds, run with -Z macro-backtrace for more info) help: consider cloning the value if the performance cost is acceptable For more information about this error, try rustc --explain E0382.

This compilation error indicates that a variable that exited the scope can't be used because Rust automatically frees its memory upon scope expiry

Rust does not perform a deep copy by default. To do one, use clone()

  • If you see clone() being used, there’s a chance some arbitrary code may be executed. Sounds fishy!

Integers have the copy() trait implemented

  • Passing variables to functions will transfer ownership
  • Returning variables in functions will also transfer ownership
  • References allow you to take up the values without taking ownership. This is called borrowing
  • References are immutable by default
  • Borrowing a mutable reference more than once is not allowed
    • Prevents compile-time data races
    • You cannot borrow a mutable reference of a variable when an immutable reference is already borrowed