Cookin' with Rust

A practical guide to the Rust crate ecosystem.

Recipes

Contributing

If you'd like to make changes to the project, please see this guide.

License

MIT/Apache-2.0

A note about error handling

Error handling in Rust is robust when done correctly, but in today's Rust it requires a fair bit of boilerplate. Because of this one often sees Rust examples filled with unwrap calls instead of proper error handling.

Since these recipes are intended to be reused as-is and encourage best practices, they set up error handling correctly, and when necessary to reduce boilerplate, they use the [error-chain] crate.

The code for this setup generally looks like:

#[macro_use]
extern crate error_chain;

mod errors {
    error_chain! {
        foreign_links {
            Io(::std::io::Error);
        }
    }
}

use errors::*;

fn main() { run().unwrap() }

fn run() -> Result<()> {
    use std::io::Write;
    let ref mut stdout = ::std::io::stdout();
    writeln!(stdout, "hello, world")?;

    Ok(())
}

This is using the error_chain! macro to define a custom Error and Result type, along with an automatic conversion from the common ::std::io::Error type. The automatic conversion makes the ? operator work

For more background on error handling in Rust, read [this page of the Rust book][error-docs] and [this blog post][error-blog].

Math

rand

Random number generators and other randomness functionality

rand-badge

Example: Monte carlo integration

Use the rand crate to generate random samples and approximate $\int_{0}^{\pi} sin(x) dx$ using monte carlo.

Key concepts:

  • Creating thread-specific RNG
  • Generating real numbers over an interval
extern crate rand;

use rand::Rng;
use std::f32;

/// f(x) = sin(x)
fn f(x: f32) -> f32 {
    x.sin()
}

/// Compute integral of f(x) dx from a to b using n samples
fn monte_carlo(a: f32, b: f32, n: u32) -> f32 {
    // Generate numbers specific to this thread
    let mut rng = rand::thread_rng();

    let mut samples: Vec<f32> = Vec::new();

    // Generate n samples between [a, b)
    for _ in 0..n {
        samples.push(rng.gen_range(a, b)); 
    }

    // Find function values
    let mut sum = 0.;
    for x in samples {
        sum += f(x);
    }
    
    // Returns average of samples over interval
    (b - a) / n as f32 * sum
}

fn main() {
    println!("{}", monte_carlo(0., f32::consts::PI, 200_000));
}

Example: Generating random RGB colors

A trait is a language feature that tells the Rust compiler about functionality a type must provide.

Rust has the powerful ability to create traits for your own types. One example is rand::Rand. Any type that implements Rand can use the polymorphic function Rng::gen() to generate random types.

Key concepts:

  • Generating a random structure
extern crate rand;

use rand::Rng;
use rand::Rand;

#[derive(Debug)] // Allows us to print using {:?} format specifier
struct Color { // RGB Color struct
    r: f64,
    g: f64,
    b: f64,
}

// Implementing Rand for type Color
impl Rand for Color {
    fn rand<R: Rng>(rng: &mut R) -> Self {
        Color {r: rng.next_f64(), b: rng.next_f64(), g: rng.next_f64()}
    }
}

fn main() {
    // Generate a random Color and print to stdout
    let mut rng = rand::thread_rng();
    let c: Color = rng.gen();
    println!("{:?}", c);
}

Byteorder

byteorder-badge

Read and write integers in little-endian byte order

extern crate byteorder;

use std::io::Cursor;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

#[derive(Default, Eq, PartialEq, Debug)]
struct Payload {
  kind: u8,
  value: u16,
}

fn run() -> Result<()> {
   let original_payload = Payload::default();
   let encoded_buf = encode(&original_payload)?;
   let decoded_payload = decode(&encoded_buf)?;
   assert_eq!(original_payload, decoded_payload);
   Ok(())
}

fn encode(payload: &Payload) -> Result<Vec<u8>> {
   let mut wtr = vec![];
   wtr.write_u8(payload.kind)?;
   wtr.write_u16::<LittleEndian>(payload.value)?;
   Ok(wtr)
}

fn decode(buf: &[u8]) -> Result<Payload> {
    let mut rdr = Cursor::new(buf);
    Ok(Payload {
        kind: rdr.read_u8()?,
        value: rdr.read_u16::<LittleEndian>()?,
    })
}

#[macro_use]
extern crate error_chain;
mod errors {
    error_chain! {
        foreign_links {
            Io(::std::io::Error);
        }
    }
}
use errors::*;
fn main() { run().unwrap() }

JSON

json-badge

JSON implementation in Rust:

The example below shows two simple ways to embed JSON in Rust. The first method parses block JSON as a block using the parse method from the json crate. It then unwraps the parsed JSON. The second method instantiates an object as JSON using the object macro. Key value relationships are easily set using =>.

After demonstrating two simple ways to write JSON, the assert_eq! macro ensures equivalence.

#[macro_use]
extern crate json;

fn main(){
    let parsed_data = json::parse(r#"

    {
        "userid": 103609,
        "verified": true,
        "access_privelages": [
            "user",
            "admin"
        ]
    }

    "#).unwrap();

    let instantiated_data = object!{
        "userid" => 103609,
        "verified" => true,
        "access_privelages" => array![
            "user",
            "admin"
        ]
    };

    assert_eq!(parsed_data, instantiated_data);
}

License

MIT/Apache-2.0

TOML

toml-badge

Parse TOML into a toml::Value and then operate on it:

extern crate toml;

fn main() {

    let toml_source = "
        [package]
        name = \"your package!\"
        version = \"0.1.0\"
        authors = [\"You! <you@example.org>\"]

        [dependencies]
        cool = \"0.2.1\"";

    let package_info = toml_source.parse::<toml::Value>().unwrap();

    assert_eq!(package_info["dependencies"]["cool"].as_str(), Some("0.2.1"));
    assert_eq!(package_info["package"]["name"].as_str(), Some("your package!"));

}

Parse TOML into your own structs using the serde crate:


extern crate toml;

#[macro_use]
extern crate serde_derive;
extern crate serde;

#[derive(Deserialize)]
struct Config {
    package: Package,
    dependencies: std::collections::HashMap<String, String>,
}

#[derive(Deserialize)]
struct Package {
    name: String,
    version: String,
    authors: Vec<String>,
}

fn main() {

    let toml_source = "
        [package]
        name = \"your package!\"
        version = \"0.1.0\"
        authors = [\"You! <you@example.org>\"]

        [dependencies]
        cool = \"0.2.1\"";

    let package_info : Config = toml::from_str(toml_source).unwrap();

    assert_eq!(package_info.package.name, "your package!");
    assert_eq!(package_info.package.version, "0.1.0");
    assert_eq!(package_info.package.authors, vec!["You! <you@example.org>"]);
    assert_eq!(package_info.dependencies["cool"], "0.2.1");

}

License

MIT/Apache-2.0

Contributing to the Rust Cookbook

Have something useful to add to the Rust Cookbook? We'd love to have it!

This document contains information and guidelines that you should read before contributing to the project. If you think something in this document should change, feel free propose a change in a pull request.

Table of Contents

Getting Started

TODO: Mention Trello and how to join (if we keep using it)

How to Contribute

TODO: Reporting bugs
TODO: Project page suggestions
TODO: Fixing bugs
TODO: Pull requests

Crates

TODO: How to add new crates to project

Tests

TODO: Write about writing tests

Style

https://aturon.github.io

Git Commit Messages

https://chris.beams.io/posts/git-commit/
TODO: Possibly take relevant parts from this post or write our own

Snippet Style

TODO: Talk about writing good idiomatic code
TODO: Maybe provide a template?