Rust ================== ## Resources - - - - Optimization: use `RUSTFLAGS="-C target-cpu=native"` to take advantage of CPU special features. (via http://vfoley.xyz/rust-compilation-tip/) For local rust/std documentation, do `rustup doc`. ## Little tricks Run tests with stdout output: cargo test -- --nocapture To run tests with logging enabled (eg, with `env_logger`), make sure you add `env_logger::init()` to the test function itself. ## map() and Result Ergonomics `.collect()` has some magical features! In addition to turning an iterator of `Item` into `Vec`, it will turn an iterator of `Result` into `Result>`. This makes it really useful for the end of functions. This is particularly useful for resolving some categories of "error handling in map closures": you can use `?` in the map closure as long as you wrap the happy path with `Ok()` and call collect on the outside. Eg: let list: Vec = junk .iter() .map(|thing| Ok(Item { a: thing.a, b: fixup(thing.widget)?, })) .collect::Result>()?; What about when `map` over an `Option`? Eg: let toy = Shiny { a: 123, b: component.map(|v| paint(v).expect("paint to succeed"), }; Should use match in this case: let toy = Shiny { a: 123, b: match component { None => None, Some(v) => Some(paint(v)?), }, }; ## 2020-05-17 Reading While working on fatcat-cli tool, checked the The Rust Programming Language book to read about trait objects and the `dyn` keyword, which I had ignored previously. They seem like they could be used in a few places in fatcat-server rust code. We don't particularly care about per-function-call performance there, and most entities are already allocated on the heap. Other small syntax and thing learned: Can copy a struct while only updating specific fields with ".." syntax. Might use this in fatcat-cli for update mutation. This is the cleanest example of using ErrorKind that I have seen: let f = match f { Ok(file) => file, Err(error) => match error.kind() { ErrorKind::NotFound => match File::create("hello.txt") { Ok(fc) => fc, Err(e) => panic!("Problem creating the file: {:?}", e), }, other_error => { panic!("Problem opening the file: {:?}", other_error) } }, }; I didn't realize that test code may get compiled into non-test binaries unless annotated with `#[cfg(test)]`. You are supposed to create a sub-module within each `src/` file with unittests, like: #[cfg(test)] mod tests { use super::*; #[test] fn it_works() { assert_eq!(2 + 2, 4); } } This doesn't apply to `tests/` directory, which is for integration tests. The common pattern for binary crates (vs. library crates) is to have `main.rs` and `lib.rs`, with any code that needs to be tested in `lib.rs` (aka, all the actual logic). I think I knew `eprintln!()` (for stderr) vs. `println!()` (for stdout), but good to remember. There is a description of how to avoid memory leaks with reference counting using "weak" `Rc` references. Probably worth reading the [entire chapter on smart pointers](https://doc.rust-lang.org/book/ch15-06-reference-cycles.html#preventing-reference-cycles-turning-an-rct-into-a-weakt) (including Box, Rc, RefCell) again. For the `Sized` trait, and `Sized` trait alone, can specify an ambiguous trait constraint with `?` to indicate "may or may not be Sized", which doesn't really mean anything but does explicitly allow generic functions over non-sized traits like: fn my_generic_func(t: &T) { // --snip-- } A trait can depend on another trait. For example, a PrettyPrint trait could rely on Display (and impl functions could call functions from Display). This is done on the trait definition line. Such a trait is called a "supertrait". Implementing Deref on a wrapper type allows transparent access to all the trait methods on the interior object. Also, a new longer post on error handling: