Konubinix' opinionated web of thoughts

Rust

Fleeting

cargo (package manager)

semver in rust cargo

specifier 0.8.5 is actually shorthand for ^0.8.5, which means any version that is at least 0.8.5 but below 0.9.0.

https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html

When you build your project in the future, Cargo will see that the Cargo.lock file exists and will use the versions specified there rather than doing all the work of figuring out versions again. This lets you have a reproducible build automatically.

https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html

When you do want to update a crate, Cargo provides the command update, which will ignore the Cargo.lock file and figure out all the latest versions that fit your specifications in Cargo.toml.

https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html

Cargo will only look for versions greater than 0.8.5 and less than 0.9.0

https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html

To use rand version 0.9.0 or any version in the 0.9.x series, you’d have to update the Cargo.toml file to look like this instead: [dependencies] rand = “0.9.0”

https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html

documentation generation

it. Another neat feature of Cargo is that running the cargo doc –open command will build documentation provided by all your dependencies locally and open it in your browse

https://doc.rust-lang.org/book/ch02-00-guessing-game-tutorial.html

memory handling

Deref coercion works only on types that implement the Deref trait.

https://doc.rust-lang.org/book/ch15-02-deref.html

Note that f32 and f64 do not implement Hash, likely because floating-point precision errors would make using them as hashmap keys horribly error-prone

https://doc.rust-lang.org/rust-by-example/std/hash/alt_key_types.html

A String is stored as a vector of bytes (Vec<u8>), but guaranteed to always be a valid UTF-8 sequence. String is heap allocated, growable and not null terminated

https://doc.rust-lang.org/rust-by-example/std/str.html

&str is a slice (&[u8]) that always points to a valid UTF-8 sequence, and can be used to view into a String, just like &[T] is a view into Vec<T>.

https://doc.rust-lang.org/rust-by-example/std/str.html

Box, Vec, String, File, and Process are some examples of types that implement the Drop trait to free resources. The Drop trait can also be manually implemented for any custom data type.

https://doc.rust-lang.org/rust-by-example/trait/drop.html

When your code calls a function, the values passed into the function (including, potentially, pointers to data on the heap) and the function’s local variables get pushed onto the stack. When the function is over, those values get popped off the stack.

https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html

We now have two variables, x and y, and both equal 5. This is indeed what is happening, because integers are simple values with a known, fixed size, and these two 5 values are pushed onto the stack.

https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html

Rust has a special annotation called the Copy trait that we can place on types like integers that are stored on the stack (we’ll talk more about traits in Chapter 10). If a type implements the Copy trait, an older variable is still usable after assignment. Rust won’t let us annotate a type with the Copy trait if the type, or any of its parts, has implemented the Drop trait. If the type needs something special to happen when the value goes out of scope and we add the Copy annotation to that type, we’ll get a compile-time error.

https://doc.rust-lang.org/book/ch04-01-what-is-ownership.html

type that implements Copy must also implement Clone, because a type that implements Copy has a trivial implementation of Clone that performs the same task as Copy.

https://doc.rust-lang.org/book/appendix-03-derivable-traits.html

dynamic typing

Instead of returning a trait object directly, our functions return a Box which contains some Animal. A box is just a reference to some memory in the heap. Because a reference has a statically-known size, and the compiler can guarantee it points to a heap-allocated Animal, we can return a trait from our function!

https://doc.rust-lang.org/rust-by-example/trait/dyn.html

trait object points to both an instance of a type implementing our specified trait as well as a table used to look up trait methods on that type at runtime. We create a trait object by specifying some sort of pointer, such as a & reference or a Box<T> smart pointer, then the dyn keyword, and then specifying the relevant trait. (We’ll talk about the reason trait objects must use a pointer in Chapter 19 in the section “Dynamically Sized Types and the Sized Trait.”) We can use trait objects in place of a generic or concrete type. Wherever we use a trait object, Rust’s type system will ensure at compile time that any value used in that context will implement the trait object’s trait. Consequently, we don’t need to know all the possible types at compile time.

https://doc.rust-lang.org/book/ch17-02-trait-objects.html

This concept—of being concerned only with the messages a value responds to rather than the value’s concrete type—is similar to the concept of duck typing in dynamically typed languages: if it walks like a duck and quacks like a duck, then it must be a duck!

https://doc.rust-lang.org/book/ch17-02-trait-objects.html

advantage of using trait objects and Rust’s type system to write code similar to code using duck typing is that we never have to check whether a value implements a particular method at runtime or worry about getting errors if a value doesn’t implement a method but we call it anyway. Rust won’t compile our code if the values don’t implement the traits that the trait objects need.

https://doc.rust-lang.org/book/ch17-02-trait-objects.html

Trait Objects Perform Dynamic Dispatch Recall in the “Performance of Code Using Generics” section in Chapter 10 our discussion on the monomorphization process performed by the compiler when we use trait bounds on generics: the compiler generates nongeneric implementations of functions and methods for each concrete type that we use in place of a generic type parameter. The code that results from monomorphization is doing static dispatch, which is when the compiler knows what method you’re calling at compile time. This is opposed to dynamic dispatch, which is when the compiler can’t tell at compile time which method you’re calling. In dynamic dispatch cases, the compiler emits code that at runtime will figure out which method to call

https://doc.rust-lang.org/book/ch17-02-trait-objects.html

When we use trait objects, Rust must use dynamic dispatch

https://doc.rust-lang.org/book/ch17-02-trait-objects.html

The compiler doesn’t know all the types that might be used with the code that is using trait objects, so it doesn’t know which method implemented on which type to call. Instead, at runtime, Rust uses the pointers inside the trait object to know which method to call. There is a runtime cost when this lookup happens that doesn’t occur with static dispatch.

https://doc.rust-lang.org/book/ch17-02-trait-objects.html

lifetime

function signatures with lifetimes have a few constraints:

any reference must have an annotated lifetime. any reference being returned must have the same lifetime as an input or be static.

https://doc.rust-lang.org/rust-by-example/scope/lifetime/fn.html

note that returning references without input is banned if it would result in returning references to invalid data

https://doc.rust-lang.org/rust-by-example/scope/lifetime/fn.html

A lifetime is a construct the compiler (or more specifically, its borrow checker) uses to ensure all borrows are valid. Specifically, a variable’s lifetime begins when it is created and ends when it is destroyed. While lifetimes and scopes are often referred to together, they are not the same.

https://doc.rust-lang.org/rust-by-example/scope/lifetime.html

Lifetime annotations don’t change how long any of the references live. Just as functions can accept any type when the signature specifies a generic type parameter, functions can accept references with any lifetime by specifying a generic lifetime parameter. Lifetime annotations describe the relationships of the lifetimes of multiple references to each other without affecting the lifetimes.

https://doc.rust-lang.org/stable/book/ch10-03-lifetime-syntax.html

how generic lifetime parameters of multiple references relate to each other

https://doc.rust-lang.org/stable/book/ch10-03-lifetime-syntax.html

Notes pointant ici