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
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
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 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
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?