Some findings and notes about using rust on some real-world stuff

Handling JSON

A small example how to dump your struct to a file and read it again.

Cargo.toml:

[dependencies]
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
use std::io::BufWriter;
use std::io::BufReader;
use std::fs::File;

#[derive(Serialize, Deserialize, Debug)]
enum CarClass {
    Race,
    Offroad,
    Hover
}

#[derive(Serialize, Deserialize, Debug)]
struct Car {
    maxspeed: f64,
    weight: f64,
    class: CarClass
}

fn main() {
    let new_car = Car {
        maxspeed: 120.0,
        weight: 4000.0,
        class: CarClass::Offroad
        };
    
    // write out the file
    let writer = BufWriter::new(File::create("car.json").unwrap());
    serde_json::to_writer_pretty(writer, &new_car).unwrap();

    // read it back again
    let reader = BufReader::new(File::open("car.json").unwrap());
    let loaded_car: Car = serde_json::from_reader(reader).unwrap();
    println!("{:?}", loaded_car);
}

Iterate folder:

https://doc.rust-lang.org/std/fs/fn.read_dir.html

use std::io;
use std::fs::{self, DirEntry};
use std::path::Path;


fn iterate_path(dir: &Path, cb: &Fn(&DirEntry)) -> io::Result<()> {
    if dir.is_dir() {
        for entry in fs::read_dir(dir)? {
            let entry = entry?;
            let path = entry.path();
            if path.is_dir() {
                visit_dirs(&path, cb)?;
            } else {
                cb(&entry);
            }
        }
    }
    Ok(())
}

or walkdir:

[dependencies]
walkdir = "2"
extern crate walkdir
use walkdir::WalkDir;

for entry in WalkDir::new("foo") {
    dbg!(entry);
}

Argument parsing with clap

[dependencies]
clap = "2"
extern crate clap;
use clap::{Arg, App, SubCommand};

fn main() {
    let matches = App::new("myapp")
                          .version("0.1")
                          .arg(Arg::with_name("dir")
                               .value_name("DIRECTORY")
                               .help("Sets a starting directory")
                               .takes_value(true)
                               .required(true)
                          )
                          .get_matches();
    
    let dir  = matches.value_of("dir").unwrap();
    println!("Value for root: {}", dir);

}
cargo run -- somedir

XML

use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use treexml::Document;

   
match File::open("yo.xml") {
    Ok(f) => {
        let mut buf_reader = BufReader::new(f);
        let doc = Document::parse(buf_reader).unwrap();
    },
    Err(_e) => ()
}


Run system command

use std::process::Command;

let output = 
Command::new("ls")
        .args(&["-l", "-R"])
        .output()
        .expect("failed to execute process");
let res = output.stdout;
println!("{:?}", res);

Gracefully handling Options and Results in iterators

fn main() {
    let animals = vec![Some("Bear"), Some("Dog"), None, Some("Cat")];
    let valid_animals = animals.iter().flat_map(|x| x).collect::<Vec<_>>()
}

Parse string to type

let i = String::from("42").parse::<i32>().unwrap();
// or let the compiler figure that out from annotating the variable
let i: i32 = String::from("42").parse().unwrap();

Defaults for structs

Derive from Default is the easiest:

#[derive(Default)]
struct Animal {
    hitpoints: i32,
    speed: f64,
    weight: f64
}
fn main() {
    // use all defaults
    let snake = Animal::default();
    //just specify one, use Default for everything else
    let birdy = Animal {
        hitpoints: 500,
        ..
        Default::default()
    };
}

If you want to have hand-crafted defaults, you need to impl your own Default:

struct Animal {
    hitpoints: i32,
    speed: f64,
    weight: f64
}

impl Default for Animal {
    fn default () -> Animal {
        Animal{hitpoints: 500, speed: 3.141, weight: 55.5}
    }
}

fn main() {
    let snake = Animal::default();
}

Building gotchas and Cargo stuff

OSX @rpath

you can use install_name_tool to add a relative library search path to your executable like that:
install_name_tool -add_rpath @executable_path/. your_binary
in case you are using a gui library like libui you can then bundle the libui.A.dylib with your app bundle.
cargo bundle is recommended for packaging the actual app.

create multiple binaries

Let's say you want a GUI app and one for the command line that has minimal size and dependencies. All you need to do is the following in your Cargo.toml:

[[bin]]
name = "cli"
path = "src/cli.rs"

[[gui]]
name = "cli"
path = "src/gui.rs"

cargo build will then build both. Run the binary of your choice with cargo run --bin cli.

Windows programs without a console window

#![windows_subsystem = "windows"]

If you use this in your crate root, no console window will be shown if you run the resulting executable.

Git dependencies

[dependencies]
rand = { git = "https://github.com/rust-lang-nursery/rand" }

But what if your repo is private? Relative url without a base?
First: if you are cloning with ssh, use the appropriate protocol and use an absolute-style url - github will accept replacing : with a /:

[dependencies]
rand = { git = "ssh://git@github.myorg.com/myuser/myrepo.git" }

In order for this to work, .cargo/config needs to specify

[net]
git-fetch-with-cli = true

This file can be in your ~ home dir or in the project itself which makes sense if other people want to build your project.