Nuggets of Rust

Nuggets of Rust

Submitted by Jitesh Doshi on Tue, 02/28/2023 - 09:02
ferris the crab - rust mascot
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. ![Rust Logo](https://foundation.rust-lang.org/img/rust-logo-blk.svg) ## 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) } ```
Tags
© 2026 SpinSpire All rights reserved.