Nuggets of Rust
Submitted by Jitesh Doshi
on Tue, 02/28/2023 - 09:02
I learned Rust long time back in a haphazard manner, and then I keep getting out of touch when I haven't had a chance to use it continuously. So here I have put together a quick cheat sheet of things to remember.
It might help someone either refresh their Rust knowledge, or learn the basics for the first time.
Special thanks to https://fasterthanli.me/articles/a-half-hour-to-learn-rust for putting together such a nice concise list. Many of the nuggets below are adapted from there.

## Resources
- https://doc.rust-lang.org/book/
- https://doc.rust-lang.org/rust-by-example/
- https://www.rust-lang.org/
- https://docs.rs/
- https://this-week-in-rust.org/
- https://readrust.net/
## Videos
- [No Boilerplate - playlist](https://www.youtube.com/playlist?list=PLZaoyhMXgBzoM9bfb5pyUOT3zjnaDdSEP)
- [The Rust Lang Book - playlist](https://www.youtube.com/playlist?list=PLai5B987bZ9CoVR-QEIN9foz4QCJ0H2Y8)
- [Rust Tutorial by Derek Banas - 2.5 hrs](https://www.youtube.com/watch?v=ygL_xcavzQ4)
- [Brad Traversy - 2 hrs](https://www.youtube.com/watch?v=zF34dRivLOw)
## Channels
- https://www.youtube.com/@codetothemoon
- https://www.youtube.com/@JeremyChone
- https://www.youtube.com/@chrisbiscardi
- https://www.youtube.com/@letsgetrusty
- https://www.youtube.com/@RyanLevicksVideos
- https://www.youtube.com/@rusting-inc
- https://www.youtube.com/@dailydoseofrustlang
- https://www.youtube.com/@HelloRust
- https://www.youtube.com/@rustbeltrust
- https://www.youtube.com/@rustlabconference3671
- [Search Query: Rust Programming channels on YouTube](https://www.youtube.com/results?search_query=rust+programming&sp=EgIQAg%253D%253D)
## Nuggets
Nuggets (tidbits) of useful knowledge gathered.
- create project with `cargo new myproject`
```
myproject/
├── Cargo.toml
└── src
└── main.rs
```
- Two types of strings
- `String` owned, growable string (is it always on the heap?)
- `&str` borrowed primitive string slice ref
- There is a [`Deref` trait](https://doc.rust-lang.org/std/string/struct.String.html#deref-methods-str) that allows String refs to be borrwed into `&str` ...
```rust
let s1: String = String::from("hi");
let s2: &str = &s1;
```
- loops
```rust
// 0..10 is just a case of something that implements std::iter::IntoIterator
for i in 0..10 {
// i iterates from 0 to 9
}
let mut xs = vec![1,2,3];
while let Some(x) = xs.pop() {
// use x
}
```
- [Writing tests](https://doc.rust-lang.org/book/ch11-01-writing-tests.html)
```rust
#[cfg(test)]
mod tests {
#[test]
fn mytestfn() {
let result = ...;
assert_eq!(result, expected)
}
}
```
- There's no ternary operator. Use `if/else` with tail expressions
```rust
let x = if(age >= 18) { "adult" } else { "minor" }
```
Or a `match` ...
```rust
match {
age < 18 => "minor",
age < 65 => "adult",
age >= 65 => "senior",
}
```
- Units of code
- crates are like libraries (modules in Go)
- modules are like source files/file-groups (packages in Go)
- `use` statement brings stuff into scope
```rust
use std::cmp::min;
let least = min(7, 1); // this is 1
```
- curly braces can contain "glob patterns"
```rust
use std::cmp::{min, max};
use std::cmp::*; // bring everything into scope
```
- methods that take `self` can be called as methods or functions
```rust
let l1 = "foo".len();
let l2 = str::len("foo");
```
- Rust implicitly includes the following at the start of every module,
which brings `Vec`, `String`, `Option`, `Result`, etc. into scope.
```rust
use std::prelude::v1::*;
```
- Structs ...
```rust
struct Vec2 {
x: f64, // 64-bit floating point, aka "double precision"
y: f64,
}
let v1 = Vec2 { x: 1.0, y: 3.0 };
let v2 = Vec2 { y: 2.0, x: 4.0 };
let v3 = Vec2 {
x: 14.0,
..v2 // spread .. must be last, and does not override previous fields
};
let v4 = Vec2 { ..v3 }; // spread .. all fields
let Vec2 {x, y} = v2; // destructuring: x = 2.0, y = 4.0
let Vec2 {x, ..} = v2; // destructuring: discard y
let v5 = Vec2{x, y:6} // same as Vec2{x:x, y:6}
```
- Methods ... can be implemented only on your types
```rust
impl Vec2 {
fn diag(&self) -> f64 {
f64::sqrt(self.x * self.x + self.y * self.y)
}
}
```
- Traits ... can be implemented on anyone's types (as long as the trait or the type is yours)
```rust
trait Signed {
fn is_strictly_negative(self) -> bool;
}
// ok, even if Number is not your type
impl Signed for Number {
// self if of type that is the target of impl-for
fn is_strictly_negative(self) -> bool {
self.value < 0
}
}
// even a primitive type
impl Signed for i32 {
fn is_strictly_negative(self) -> bool {
self < 0
}
}
```
- marker traits (e.g. `Copy`) have no methods, but they indicate that a type has properties (e.g. copy-itself).
```rust
let a:i32 = 5;
let b = a; // copied!
let c: Number = ...;
let d = c; // moved!
```
- Some traits can be implemented with `#[derive]` attribute.
```rust
#[derive(Clone, Copy)]
struct Number {
odd: bool,
value: i32,
}
```
- Generics ...
```rust
// trait Display is a constraint of T
fn print(value: T) {
println!("value = {}", value);
}
// trait Debug is a constraint of T
fn print(value: T) {
println!("value = {:?}", value);
}
fn print(value: T)
where
T: Display + Debug, // multiple constraints
{
// ...
}
```
- compile time reflection? (`::<>` is called turbofish syntax)
```rust
fn main() {
use std::any::type_name;
println!("{}", type_name::()); // prints "i32"
println!("{}", type_name::<(f64, char)>()); // prints "(f64, char)"
}
```
- `Option` is an enum, with two variants. So is `Result`. Both can be `unwrap`d. But better yet, use `expect` to fail with a custom message.
```rust
enum Option {
None,
Some(T),
}
fn main() {
let o1: Option = Some(128);
o1.unwrap(); // this is fine
let o2: Option = None;
o2.unwrap(); // this panics!
}
enum Result {
Ok(T),
Err(E),
}
```
- Function reference parameter lifetimes ... Named lifetimes allow returning references whose lifetime depend on the lifetime of the arguments. There is a special lifetime, named `'static`, which is valid for the entire program's lifetime.
```rust
// elided (non-named) lifetimes:
fn print(x: &i32) {}
// named lifetimes:
fn print<'a>(x: &'a i32) -> &'a i32{}
// not really needed in this case, since this is default.
```
- Structs can also be generic over lifetimes, which allows them to hold references
```rust
struct NumRef<'a> {
x: &'a i32,
}
fn main() {
let x: i32 = 99;
let x_ref = NumRef { x: &x };
// `x_ref` cannot outlive `x`, etc.
}
```
- For many types in Rust, there are owned and non-owned variants:
- Strings: `String` is owned, `&str` is a reference
- Paths: `PathBuf` is owned, `&Path` is a reference
- Collections: `Vec` is owned, `&[T]` is a reference
## Pearls
Pearls (real gems) of wisdom gathered.
- If you want to do some complex computation and use the result, you don't have to write a function or IIFE, etc. Just use the "tail" of a block.
```rust
let x = {
let y = 1; // first statement
let z = 2; // second statement
y + z // this is the *tail* - what the whole block will evaluate to
};
```
- `let` (destructuring) patterns can be used as conditions in `if`. And so can `match` arms (though a `match` has to be exhaustive; use `_` as the catch-all arm):
```rust
if let Number { odd: true, value } = n {
println!("Odd number: {}", value);
} else if let Number { odd: false, value } = n {
println!("Even number: {}", value);
}
match n {
Number { odd: true, value } => println!("Odd number: {}", value),
Number { odd: false, value } => println!("Even number: {}", value),
}
```
- The generic part of a type can be inferred from usage ...
```rust
fn main() {
let mut v1 = Vec::new();
v1.push(1); // v1 is now Vec
let mut v2 = Vec::new();
v2.push(false); // v2 is now Vec
}
```
- While borrowed, a variable binding cannot be mutated:
```rust
fn main() {
let mut x = 42;
let x_ref = &x;
x = 13;
println!("x_ref = {}", x_ref);
// error: cannot assign to `x` because it is borrowed
}
```
- While immutably borrowed, a variable cannot be mutably borrowed:
```rust
fn main() {
let mut x = 42;
let x_ref1 = &x;
let x_ref2 = &mut x;
// error: cannot borrow `x` as mutable because it is also borrowed as immutable
println!("x_ref1 = {}", x_ref1);
}
```
- Use `?` to forward error ... Think of it like a `throws` clause from Java where you're passing the buck to the caller by declaring what kind of error this function could produce.
```rust
fn foo() -> Result {
let x = some_fn_that_could_err()?;
Ok(x)
}
```
