# Rust
## See also
### - [[Async Rust]]
### - [[Rust - Traits]]
## Resources
**What is Rust?**
- [What is Rust and why is it so popular?](https://stackoverflow.blog/2020/01/20/what-is-rust-and-why-is-it-so-popular/) (Blog post, January 2020)
**Learning Rust**
- [Rust website > Learn Rust](https://www.rust-lang.org/learn)
- ["The Rust Programming Language"](https://doc.rust-lang.org/book/title-page.html): (**The** Rust book)
- [Rust by Example](https://doc.rust-lang.org/stable/rust-by-example/index.html)
- [rustlings](https://github.com/rust-lang/rustlings)
- [proc-macro-workshop](https://github.com/dtolnay/proc-macro-workshop) by [[David Tolnay]]: *A selection of projects designed to learn to write Rust procedural macros.*
- [Learn Rust With Entirely Too Many Linked Lists](https://rust-unofficial.github.io/too-many-lists/index.html#learn-rust-with-entirely-too-many-linked-lists)
- [Rust &TheMachine: Concretely subdue the compiler and make it work for you](https://medium.com/@orbitalK/why-the-machine-b9803a77fa29)
- [Rust concepts I wish I learned earlier](https://rauljordan.com/rust-concepts-i-wish-i-learned-earlier/)
**Learning Async Rust**
- [Tokio Tutorial](https://tokio.rs/tokio/tutorial)
- Tokio Topics
- [Bridging with sync code](https://tokio.rs/tokio/topics/bridging)
- [Graceful Shutdown](https://tokio.rs/tokio/topics/shutdown)
- [Getting started with Tracing](https://tokio.rs/tokio/topics/tracing)
- [Next steps with Tracing](https://tokio.rs/tokio/topics/tracing-next-steps)
- Blog posts by Alice Ryhl @Darksonn:
- [Async: What is blocking?](https://ryhl.io/blog/async-what-is-blocking/)
- [Actors with Tokio](https://ryhl.io/blog/actors-with-tokio/)
**General references**
- [`std`](https://doc.rust-lang.org/std/index.html): Standard library documentation
- [`core`](https://doc.rust-lang.org/core/index.html): Rust core library ([What is the rust core crate??](https://www.reddit.com/r/rust/comments/bpmy21/what_is_the_rust_core_crate/))
- [stdrs.dev](https://stdrs.dev/) : Standard library internal documentation
- [`libc`](https://docs.rs/libc/latest/libc/): Raw bindings to platforms' system libraries
- [`nix`](https://docs.rs/nix/latest/nix/): Safe bindings to `libc` for nix platforms
- [The Rust Reference](https://doc.rust-lang.org/reference/introduction.html): All the nitty gritty details of Rust
- [Rust Cookbook](https://rust-lang-nursery.github.io/rust-cookbook/intro.html): More examples
- [The Cargo Book](https://doc.rust-lang.org/cargo/index.html): *A book on Rust’s package manager and build system.*
- [Rust Compiler Error Index](https://doc.rust-lang.org/error-index.html)
- [The rustdoc book](https://doc.rust-lang.org/rustdoc/index.html)
- [The rustc book](https://doc.rust-lang.org/rustc/index.html)
- [The Edition Guide](https://doc.rust-lang.org/edition-guide/introduction.html)
- [The Rust Style Guide](https://doc.rust-lang.org/style-guide/index.html)
- [Proptest book](https://altsysrq.github.io/proptest-book/intro.html)
- [Rust API Guidelines book](https://rust-lang.github.io/api-guidelines/about.html)
- [Rust Design Patterns book](https://rust-unofficial.github.io/patterns/intro.html)
- [Rust RFC book](https://rust-lang.github.io/rfcs/introduction.html)
- [The Unstable book](https://doc.rust-lang.org/nightly/unstable-book/index.html)
**Rust blogs**
- [Blog](https://ryhl.io/) by Alice Ryhl ([@Darksonn](https://github.com/darksonn)), [[Tokio]] maintainer
- [Baby Steps](https://smallcultfollowing.com/babysteps/) by Niko Matsakis ([@nikomatsakis](https://github.com/nikomatsakis)), core contributor to Rust
**Application-specific references**
- [Command line book](https://rust-cli.github.io/book/index.html)
- [WebAssembly book](https://rustwasm.github.io/docs/book/)
- [Embedded Rust Book](https://doc.rust-lang.org/stable/embedded-book/)
- [Writing an OS in Rust](https://os.phil-opp.com/): Blog series / tutorial
- [Microocontrollers books](https://docs.rust-embedded.org/discovery/)
**Advanced references**
- [The Rustonomicon](https://doc.rust-lang.org/nomicon/index.html): *The Rustonomicon is your guidebook to the dark arts of unsafe Rust. It’s also sometimes called “the ’nomicon.”*
- [Rust's Unsafe Code Guidelines Reference](https://rust-lang.github.io/unsafe-code-guidelines/): *This document is produced by the UCG WG to provide a "guide" for writing unsafe code that "recommends" what kinds of things unsafe code can and cannot do, and that documents which guarantees unsafe code may rely on.*
**Advanced project-specific books**
- [Guide to Rustc Development](https://rustc-dev-guide.rust-lang.org/about-this-guide.html): *This guide is meant to help document how rustc – the Rust compiler – works, as well as to help new contributors get involved in rustc development.*
- [Chalk book](https://rust-lang.github.io/chalk/book/): *Chalk is a library that implements the Rust trait system.*
- [Redox Operating System book](https://doc.redox-os.org/book/ch01-01-welcome.html): *Redox is a general purpose operating system written in pure Rust.*
**Build reproducibility**
- See [[Rust reproducibility]]
**Code style guides**
- [[rust-analyzer]]'s [style guide](https://github.com/rust-lang/rust-analyzer/blob/master/docs/dev/style.md) ([permalink](https://github.com/rust-lang/rust-analyzer/blob/c552e5a55f13b2f08d506bb46fb74dbc11702d0d/docs/dev/style.md))
**Blog posts on API design**
- Blog posts by [[Ted Kaminski]] (not actually Rust-specific)
- [The one ring problem: abstraction and our quest for power](https://www.tedinski.com/2018/01/30/the-one-ring-problem-abstraction-and-power.html)
- [System boundaries: the focus of design](https://www.tedinski.com/2018/02/06/system-boundaries.html)
## Install
### Install Rust on any Unix-like OS
```bash
# Install rustup
# Default configuration (1) is usually fine
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Test rust has been installed correctly
rustc --version
cargo install ripgrep # (Optional) checks gcc linker error
cargo install sccache # (Optional) checks pkg-config/libssl-dev error
```
If there is a `error: linker `cc` not found` error, you need to install the gcc toolchain, e.g.
```bash
sudo apt install -y build-essential
```
If you get `Could not find directory of OpenSSL installation`, you probably need `pkg-config` and `libssl-dev`, e.g.:
```bash
sudo apt install -y pkg-config libssl-dev
```
### Install nightly Rust and use for current project
```bash
# Install nightly rust
rustup install nightly
# Use the nightly toolchain when inside the current project, but leave global settings unchanged
rustup override set nightly
```
## Notes
### [Generic parameter is never used](https://stackoverflow.com/questions/69585343/why-do-i-use-generic-in-the-where-clause-but-the-compiler-still-reports-it-not?rq=1)
```rust
enum LxListener<CM, PS>
where
CM: LexeChannelManager<PS>,
PS: LexePersister,
{
ChannelMonitor(ChannelMonitorChainListener),
ChannelManager(CM),
}
```
```bash
error[E0392]: parameter `PS` is never used
--> lexe-ln/src/sync.rs:295:21
|
295 | enum LxListener<CM, PS>
| ^^ unused parameter
|
= help: consider removing `PS`, referring to it in a field, or using a marker such as `std::marker::PhantomData`
= help: if you intended `PS` to be a const parameter, use `const PS: usize` instead
```
Try defining the extra trait bound on the `impl` only:
```rust
enum LxListener<CM>
{
ChannelMonitor(ChannelMonitorChainListener),
ChannelManager(CM),
}
impl<CM, PS> Listen for LxListener<CM>
where
CM: LexeChannelManager<PS>,
PS: LexePersister,
{
}
```
Alternatively, use `PhantomData`:
```rust
enum LxListener<CM, PS> {
ChannelMonitor(ChannelMonitorChainListener),
ChannelManager(CM),
// Prevents Rust error E0392
#[allow(dead_code)]
Phantom(PhantomData<PS>),
}
```
More info:
- (Stack Overflow) [Why do I use generic in the where clause, but the compiler still reports it not used?](https://stackoverflow.com/questions/69585343/why-do-i-use-generic-in-the-where-clause-but-the-compiler-still-reports-it-not)
- (Rustonomicon) [`PhantomData`](https://doc.rust-lang.org/nomicon/phantom-data.html)
- (`std` Rust docs) [`PhantomData`](https://doc.rust-lang.org/std/marker/struct.PhantomData.html)
### [Returning errors early in iterator chains](https://doc.rust-lang.org/rust-by-example/error/iter_result.html#fail-the-entire-operation-with-collect)
A `Vec<Result<T, E>>` can be turned into a `Result<Vec<T>, E>` using `collect()`. This is akin to using the ? operator
```rust
let strings = vec!["tofu", "93", "18"];
let numbers: Result<Vec<_>, _> = strings
.into_iter()
.map(|s| s.parse::<i32>())
.collect();
println!("Results: {:?}", numbers);
```
If there are multiple nested `Result`s, consider using `Result::flatten`.
```rust
let handles: Vec<JoinHandle<anyhow::Result<Wallet>>> = ...;
let joined = future::join_all(handles).await;
let inner = joined
.into_iter()
.map(|res| res.context("Tokio task panicked").flatten())
.collect::<anyhow::Result<Vec<Wallet>>>()
.context("Failed to init wallet")?;
```
- In this example, the `Vec<JoinHandle<anyhow::Result<T>>>` is equivalent to a `Vec<Result<Result<T, E>, F>`
- Calling `.context("Tokio task panicked")` converted the `JoinError` into an `anyhow::Error` and thus the entire result into an `anyhow::Result<T>` (since `Result<T, anyhow::Error>` is equivalent to `anyhow::Result<T>`)
### [Whether a fn should take `Arc<T>`, `&Arc<T>`, or `&T`](https://www.reddit.com/r/rust/comments/lfxy6b/comment/gmoky18/?utm_source=share&utm_medium=web2x&context=3)
Contrary to `shared_pointer`, its easy in Rust to just _move_ an `Arc<T>` around without need to touch the reference counts. So it basically amounts to this:
- If a function always needs to own its own copy of the Arc, pass `Arc<T>` directly by value: The caller can decide wether to clone or to just move an existing Arc into it.
- If the function just very rarely needs to make a copy of the Arc, `&Arc<T>` can make sense so that you are not forced to do atomic operations in the common case, at the cost of not being able to just move the arc in the uncommon case.
- If the function just wants to read from the `T`, just pass `&T`.
### [Ensure that spawned child processes are killed when their handle is dropped](https://stackoverflow.com/a/30540177)
Update [[2022-06-29]]: Tokio's `tokio::process::Command`
- Has a `kill_on_drop()` method that implements this automatically
- Is async native; i.e. `child.wait()` returns a `Future` that you can `.await` on
- Has a much more ergonomic command builder api than the `std` version
- Note: Using this requires the `process` feature flag
```rust
/// ChildGuard wraps Child to kill the Child if the ChildGuard is dropped
struct ChildGuard {
child: Child
}
impl Drop for ChildGuard {
fn drop(&mut self) {
// Kill the child (•̀ᴗ•́)و ̑̑
match self.child.kill() {
Ok(()) => println!("Successfully killed child process"),
Err(e) => println!("Could not kill child process: {}", e),
}
}
}
```
### Code examples in doc comments
May require wrapping in an async block like so: (note the `#` in front of `async fn`)
![[Screen Shot 2021-08-06 at 7.26.55 PM.png]]
End result looks like this:
![[Screen Shot 2021-08-06 at 7.26.40 PM.png]]
### Logging - Medium Guide
https://medium.com/nerd-for-tech/logging-in-rust-e529c241f92e
- Basically, use crates `log` and `envlogger` and add `env_logger::init();` into the init code
- ~~If you want to write to file, use crate `log4rs`~~
- Update 2022: `flexi-logger` is best
### Anyhow
https://docs.rs/anyhow/latest/anyhow/index.html
- Is less strict than `thiserror`, use if you want something that is basically like `Box<dyn std::Error>` where you don't have to define all the different kinds of errors that can occur (e.g. if you're not developing a library)
- Add context to errors with `.context` as in the example below
- The macros are helpful:
- `anyhow!`: Construct an ad-hoc error from a string or existing non-anyhow error value.
- `bail`: Return early with an error.
- `ensure`: Return early with an error if a condition is not satisfied.
```rust
let order_update = order_update
.context("Websocket listening error")?
.ok_or_else(|| format_err!("No data returned"))?;
```
### Use nightly build for just one project
- Create a file called `rust-toolchain` which contains `nightly`
- Cargo build / run will automatically use the nightly build
- Check that it was specified correctly: `rustup show`
- Update frequently: `rustup update`
### Assess trustworthiness of dependencies
https://github.com/crev-dev/cargo-crev
### Using a Rust build script
https://doc.rust-lang.org/rust-by-example/cargo/build_scripts.html
e.g. with [[tonic]] [[gRPC]]
```toml
[package]
...
build = "build.rs"
[dependencies]
...
[build-dependencies]
tonic-build = "0.5.2"
```
Then place a `build.rs` at the project root:
```rust
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::compile_protos("proto/lightning.proto")?;
Ok(())
}
```
Then `cargo build` will run `build.rs` just prior to building the rest of the project.
### Difference between `FnOnce`, `FnMut`, `Fn`
The traits each represent more and more restrictive properties about closures/functions, indicated by the signatures of their `call_...` method, and particularly the type of `self`:
- [`FnOnce`](https://doc.rust-lang.org/std/ops/trait.FnOnce.html) (`self`) are functions that can be called once
- [`FnMut`](https://doc.rust-lang.org/std/ops/trait.FnMut.html) (`&mut self`) are functions that can be called if they have `&mut` access to their environment
- [`Fn`](https://doc.rust-lang.org/std/ops/trait.Fn.html) (`&self`) are functions that can be called if they only have `&` access to their environment
A closure `|...| ...` will automatically implement as many of those as it can.
- All closures implement `FnOnce`: a closure that can't be called once doesn't deserve the name. Note that if a closure only implements `FnOnce`, it can be called only once.
- Closures that don't move out of their captures implement `FnMut`, allowing them to be called more than once (if there is unaliased access to the function object).
- Closures that don't need unique/mutable access to their captures implement `Fn`, allowing them to be called essentially everywhere.
### Run tests using a single thread
- Removes test failures caused by race conditions
- Relevant to the FTX lib
```bash
RUST_BACKTRACE=0 cargo test -- --test-threads 1
```
### Show logs during testing
```bash
cargo test -- --nocapture
# With a specific test
cargo test rest::tests::cancel_by_client_id -- --nocapture
```
Make sure the logger is initialized somewhere though, oftentimes just temporarily at the start of the test
```rust
env_logger::init();
```
### Allow a variable to be either `Iter<T>` or `Rev<Iter<T>>` using `Either`
https://users.rust-lang.org/t/returning-an-iterator-and-reverse-iterator-with-match-statement/30194
```rust
let orders_iter = match int.sides.spot_limit {
Side::Buy => Either::Left(state.spot_orderbook.bids.iter().rev()),
Side::Sell => Either::Right(state.spot_orderbook.asks.iter()),
};
```
### Build caching
See [[sccache]]
### Cargo config file
https://doc.rust-lang.org/cargo/reference/config.html
Cargo allows local configuration for a particular package as well as global configuration. It looks for configuration files in the current directory and all parent directories. If, for example, Cargo were invoked in `/projects/foo/bar/baz`, then the following configuration files would be probed for and unified in this order:
- `/projects/foo/bar/baz/.cargo/config.toml`
- `/projects/foo/bar/.cargo/config.toml`
- `/projects/foo/.cargo/config.toml`
- `/projects/.cargo/config.toml`
- `/.cargo/config.toml`
- `$CARGO_HOME/config.toml` which defaults to:
- Windows: `%USERPROFILE%\.cargo\config.toml`
- Unix: `$HOME/.cargo/config.toml`
### Named arguments from environment
- RFC: https://rust-lang.github.io/rfcs/2795-format-args-implicit-identifiers.html
- Tracking issue: https://github.com/rust-lang/rust/issues/67984
- (Mostly done as of Nov 2021)
- Stabilization PR: https://github.com/rust-lang/rust/pull/90473
- [[Stack Overflow]] answer where I found out about it: https://stackoverflow.com/questions/45356898/is-there-a-way-to-pass-named-arguments-to-format-macros-without-repeating-the-va
### Convert `Vec<u8>` to string
https://stackoverflow.com/questions/19076719/how-do-i-convert-a-vector-of-bytes-u8-to-a-string
```rust
use std::str;
fn main() {
let buf = &[0x41u8, 0x41u8, 0x42u8];
let s = match str::from_utf8(buf) {
Ok(v) => v,
Err(e) => panic!("Invalid UTF-8 sequence: {}", e),
};
println!("result: {}", s);
}
```
Can also use `String::from_utf8` which has the same output as `str::from_utf8`
Or for a infallible version:
`String::from_utf8_lossy(&rev.stdout)`
### `Ord`, `PartialOrd`, `Eq`, and `PartialEq`
**Main points:**
- `Ord` is a total ordering and requires implementing a `cmp` method.
- `Ord` requires `PartialOrd` and `Eq` (which requires `PartialEq`)
- `PartialOrd` is a partial ordering - the required method `partial_cmp` returns an `Option<Ordering>`.
- `Eq` is a full equivalence relation and does not have any required methods. `Eq` just tells the compiler that it is indeed a equivalence relation
- `Eq` requires `PartialEq`
- `Eq` can be derived for a struct, but it requires that all fields of the struct also implement `Eq`. Sometimes this is not desirable, such as when you only want to compare based on **some** of the fields of the struct.
- `PartialEq` is a partial equivalence relation and requires implementing `eq`, which returns `bool`.
**More information:**
- [How can I implement `Ord`?](https://doc.rust-lang.org/std/cmp/trait.Ord.html#how-can-i-implement-ord)
- [How can I implement `PartialOrd`?](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html#how-can-i-implement-partialord)
- [How can I implement `Eq`?](https://doc.rust-lang.org/std/cmp/trait.Eq.html#how-can-i-implement-eq)
- [How can I implement PartialEq?](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html#how-can-i-implement-partialeq)
- [`std::cmp::Ordering`](https://doc.rust-lang.org/std/cmp/enum.Ordering.html#method.then_with)
- `Ord`, `PartialOrd`, `Eq`, and `PartialEq` in practice: [Example implementation for elements in a `BinaryHeap` from the rust docs](https://doc.rust-lang.org/std/collections/binary_heap/index.html#examples)