~irimi1/learning-rust

ref: 0330a7b576c3675c4944fd6d604bc7ef8eb1d7db learning-rust/notes.md -rw-r--r-- 6.9 KiB
0330a7b5Manuel Groß Add notes for minigrep 2 years ago

#Notes

These are things from the book I want to have in a document to quickly find again. This means I don’t write down things I have or would want to look up in the documentation or book again anyways.

#Chapter 1

  • Rust macros are called with a ! at the end of its name.
  • cargo check can be used to check if the code compiles, but is much faster than actually producing a binary.

#Chapter 2

  • Rust has a prelude—a list of things that is imported into every single rust program.
  • Cargo’s dependencies section uses Semantic Versioning.
  • Cargo can build and serve documentation for a project’s depencencies locally by running cargo doc --open.
  • Rust allows shadowing, which means re-using a variable name e.g for type conversions.
  • Variable types are annotated with a colon, e.g. let number: u32 = ….
  • The underscore is used as a catchall value, e.g. in Err(_) to catch any errors when using match.

#Chapter 3

#Variables

  • You can use the prefix r# in variable or function names when things from C libraries for example are named as a keyword from Rust.
  • const types must always be annotated.
  • Rust doesn't catch “index out of bounds” errors from arrays during compile time. Instead, it will “panic” during runtime, before any memory has been accessed.

#Functions

  • Rust’s convention for function names is snake_case.
  • Rust separates “statements“, which do not return a value, and “expressions“, which do.
  • Assignments are not expressions.
  • Expressions don’t have a semicolon at the end.
  • The last expression of a function in Rust is implicitly the return value, although you can use return to do so.
  • When the compiler says (), it means “no value” (it’s an empty tuple).

#Control Flow

  • Conditions, i.e. for if expressions, must return a bool. There’s no implicit conversion e.g. from integers or so.
  • Because if is an expression, it can be used in let statements.
  • Collections can be iterated using for element in array.iter() {…. It’s safer and faster than for example doing so with while.
  • A loop to be run a certain amount of times could be a for loop with a Range: for number in (1..25).rev() {….

#Capter 4

#Ownership

  • Where a scope ends, the drop function for all the variables used in it is called so the memory gets deallocated.
  • Binding the same value in different variables is different between simple and complex types. Simple types get copied, while for complex types, the pointer to the content is copied.
  • If a complex type is copied, the original one is no longer valid to avoid double free errors. This is effectively a move of the variable.
  • Rust never automatically creates “deep” copies of data, which is inexpensive in terms of runtime performance.
  • “Deep” copies can be created using clone.
  • Internally, this concept is implemented using the conflicting traits Copy and Drop.
  • Passing a variable to a function will also move or copy it.
  • References circumvent the problem of havin to pass variables back in order to use them again, because they would get out of scope otherwise. They allow to use a value without taking ownership of it. This is called borrowing, because e.g. a function can use the value, but not drop it.
  • In order to mutate it, references have to be declared mutable as well.
  • Only one mutable reference to a variable is allowed per scope to prevent data races.
  • A slice is a data type that does not have ownership.
  • A string slice is a reference to part of a String.
  • String literals are slices.
  • Slices work for all collections.

#Chapter 5

  • Rust doesn’t allow to mark only certain fields of a struct as mutable. The whole struct has to be.
  • Rust has automatic referencing and dereferencing, so there’s no need for a -> operator on structs.

#Chapter 6

  • Rust does not have a null, and the Option enum is a way to compensate that. This enum is included in the prelude.
  • Match compares the return value of the given expression to arbitrary patterns in the arms, and executes the code associated with the first one it matches. This code then itself needs to return something for the entire match expression.
  • Single enum variants can hold additional values.
  • _ is a “catchall” in match expressions. Since match is exhaustive, it helps covering any possible match without listing them one by one.
  • if let is a shortcut for matches where only one arm is interesting to us.

#Chapter 7

  • Everything is private by default.
  • Structs with private members require public functions to create an instance of them.

#Chapter 8

#Vectors

  • A String is a collection of characters.
  • &v[100] returns a reference to a vector’s element at 100 and crashes if that index doesn’t exist, while v.get(100) returns an Option which can be matched.

#Strings

  • String is not part of the core language, but rather of Rust’s standard library.
  • String is a wrapper over Vec<u8>.
  • The data in Strings is hold as bytes, which does not always corelate to characters.

#Hash Maps

  • get returns an option.
  • insert by default replaces values of keys that already exist.

#Chapter 9

  • Rust separates errors in recoverable ones (Result) and unrecoverable ones (panic!).
  • Result and its variants are part of the prelude.
  • For functions that return a Result, the ? operator can be used as as shorthand for returning either Ok or panicing with Err.

#Chapter 10

#Generics

  • Generics are a tool for reducing code duplication.
  • Generics don’t lead to slower code compared to concrete types (the duplication is done by the compiler, if you will).

#Traits

  • Where Rust has traits, other languages might have interfaces. It’s a concept to tell the compiler about the functionality of certain types in an abstract way.
  • You can have default implementations.
  • There is lots of syntax sugar involved as far as traits are concerned.

#Lifetimes

  • Lifetimes exist in Rust to avoid dangling references and make sure references will become invalid at a certain point.
  • There is a set of lifetime elision rules which automatically apply reoccuring lifetime patterns if none are explicitly specified.
  • TODO: I didn’t completely understand this from the book and the examples. I should find another source to get this explained in a way I understand better.

#Chapter 11

  • Tests fail when something in a test function panics.
  • The assert! macro evaluates a boolean value and panics if it’s false.
  • All the assert! macros also take optional parameters to print custom error messages.
  • Adding #[should_panic] after ´#[test]` can be used to check if the error handling works correct.

#Chapter 12

  • Per convention, programs should be split into a main.rs and a lib.rs, where the latter contains all the logic, and the main handles running the program.