“Yeah I think I got to the limit of my bargaining … getting to end of May, and when I didn’t seem to get past my last best day which was 2 weeks ago” It was May 25th, the day I remember grieving for my lost life, text chatting with my flu buddy about the five stages of grief: denial, anger, bargaining, depression and acceptance.

“I think the thing is that I have to learn to live with this — I can’t just keep expecting, waiting to suddenly get better.” I stopped making plans beyond “rest, walk a little, rest, eat a little, rest…” in achingly slow motion, on a good day I could send an email and talk on the phone to a friend.

It’s not that I gave up hope, it’s just that thinking about any specific hope for the future was just not helpful. Recently, my brother shared a poem that captured this experience well:

My grandmother once gave me a tip:
In difficult times, you move forward in small steps.
Do what you have to do, but little by little.
Don’t think about the future, or what may happen tomorrow.
Wash the dishes.
Remove the dust.
Write a letter.
Make a soup.
You see?
You are advancing step by step.
Take a step and stop.
Rest a little.
Praise yourself.
Take another step.
Then another.
You won’t notice, but your steps will grow more and more.
And the time will come when you can think about the future without crying.

— Elena Mikhalkova, The Room of Ancient Keys

I couldn’t find anything else written by Elena Mikhalkova that has been translated into English. I’d love to hear about it if her books or any of her other poems is ever translated.

A Trait in the Rust programming language enables what today’s coders commonly call “duck-typing” (walks like a duck and quacks like a duck).

In Rust, type refers to concrete types — the type of a value; whereas, a Trait refers to an abstract or generic type. Here the English word type lacks the specificity we need to describe these concepts, so we need adjectives to differentiate.

TLDR: traits vs types

The type keyword lets us define a type alias, like:

type Population = i32;

This is useful if I’m often passing around specific variables for a Population, and I have function that takes multiple numbers, then the compiler will be able to catch certain classes of errors:

fn report(p: Population, num: i32)

A Trait is a collection of functions that can be applied to a type (a built-in type, like i32 or a type that we have defined with a struct, enum or type alias). A good example of a Trait is ToString which is part of the Rust standard library:

pub trait ToString {
    fn to_string(&self) -> String;
}

Here’s a naive approach to implementing ToString on a custom struct:

struct Monster {
  eyeball_count: i32,
}

impl ToString for Monster {
  fn to_string(&self) -> String {
    format!("{}-eyed {}", self.eyeball_count, "monster")
  }
}

fn main() {
  let m = Monster { eyeball_count: 3 };
  println!("We just created a {}!", m.to_string())
}

Experienced Rustaceans would rarely implement the above code, instead they might implement std::fmt::Display which provides additional functionality we probably want, so if I write this instead of impl ToString for Monster:

impl fmt::Display for Monster {
  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    write!(f, "{}-eyed {}", self.eyeball_count, "monster")
  }
}

Then our code that calls to_string will still work, but we can also provide the variable directly to println! (or format!) and it will also work:

  println!("We just created a {}!", m);

This is because Rust allows for implementing functions on Traits in terms of a generic type.

Traits in context

Traits help break down what’s needed and make our code reusable and easier to maintain. Most libraries will use many Traits from the std libary, as well as their own Traits, which can make the learning curve a bit steep, but very flexible once you understand what they do.

So, as I’m learning, it helps me to spell everything out, but then I end up with code that was a bit hard to read. Check out this beauty that I wrote yesterday:

async fn read_some<R: AsyncRead + Send + Unpin>(mut reader: R) -> Result<(), std::io::Error>

I’m using Rust 1.39 with built-in async fn which means that the compiler will build me a future and I can declare as the return value to be whatever type that Future will ultimately produce (or simply what my function returns). In this case, I want to be able to pass in a tokio::net::TcpStream and also a “ReadHalf” that is returned from split.

My first attempt at refactoring was to do this:

type Reader = AsyncRead + Send + Unpin;

The above code doesn’t do what I wanted. I’ve explained above that type creates an alias for a concrete type; however, when we provide Traits, it (unexpected for me) creates a “Trait object” which is not the abstract type I was looking for. What I wanted to do was to define a new Trait that composes the other traits, but has no implementation of its own. Here’s the syntax I was looking for:

trait Reader: AsyncRead + Send + Unpin { } 
impl<T: AsyncRead + Send + Unpin> Reader for T {}

which I can then use like this:

async fn read_some<R: Reader>(mut reader: R) -> Result<(), std::io::Error>

or in a slightly more readable form with where:

async fn read_some<R>(mut reader: R) -> Result<(), std::io::Error>
where R: AsyncReader  

If you want to see the working code, I have a few examples here: https://github.com/ultrasaurus/rust-tokio-proxy where each can be executed like this: cargo run --example reader-type


Many thanks to the Alice, David, Lucio and Jeb on Tokio discord who helped me understand types, traits and how they are used in tokio!

I want to write a library in Rust that can be called from C and just as easily called from Rust code. The tooling makes it pretty easy, but I had to look in a few places to figure how it is supposed to work and get tests running in both languages.

C library

To focus on the process of building and testing, the library will have a single function that adds two numbers. I wrote it in pure C first:

lib.c

int add(int a, int b) {
  return a + b;
}

lib.h

int add(int a, int b);

main.c

#include <stdio.h>
#include "lib.h"

int main() {
    int sum = add(1,2);
    printf("1 + 2 = %d\n", sum);
}

one-liner to compile and run the app

gcc *.c -o app && ./app

output: 1 + 2 = 3

then I wrote a simple automated test, based on tdd blog post

Rust library

cargo new add --lib

replace lib.rs with

#[no_mangle]
pub extern "C" fn add(a: i32, b:i32) -> i32 {
    a + b
}


#[cfg(test)]
mod tests {
    #[test]
    fn it_works() {
        use crate::add;
        assert_eq!(add(2,2), 4);
    }
}

build and run with cargo test
which should have output like

$ cargo test
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running target/debug/deps/add-45abb08ccefdc53c

running 1 test
test tests::it_works ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

   Doc-tests add

running 0 tests

test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out

Compile as static library

Add to Cargo.toml

[lib]
name = "add"
crate-type = ["staticlib"]  

Now cargo build generates a compiled library file: target/debug/libadd.a. I could have stopped there, but I expect to iterate on this a bit and I had read about a crate that generates the C header file…

Generate a header (command-line)

First, install the lovely cbindgen crate (using –force just to make sure everything is up to date with the latest):

cargo install --force cbindgen

the command-line tool is pretty neat:

touch cbindgen.toml    # can be empty for defaults
cbindgen --config cbindgen.toml --crate add --output add.h

The above command will generate “add.h” file at the root of the crate.

Generate a header (cargo build)

I prefer to have the header generation integrated with cargo build (or at least I think I will). Here are the steps:

Add to Cargo.toml:

[build-dependencies]
cbindgen = "0.12"

By default, the header file is buried in target/debug/build/add-... with a bunch of intermediary build files. I find that it is nice to put it at the root of my crate where it is easy to find. Below is a custom build file that puts it in the crate root (aka CARGO_MANIFEST_DIR, the directory that Cargo.toml is in).

build.rs:

extern crate cbindgen;

use std::env;
use std::path::PathBuf;


fn main() {
  let crate_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR")
        .expect("CARGO_MANIFEST_DIR env var is not defined"));

  let config = cbindgen::Config::from_file("cbindgen.toml")
        .expect("Unable to find cbindgen.toml configuration file");

  cbindgen::generate_with_config(&crate_dir, config)
        .expect("Unable to generate bindings")
        .write_to_file(crate_dir.join("add.h"));
}

As mentioned above, cbindgen.toml may be empty, but here’s some settings I like:

include_guard = "add_h"
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
language = "C"
includes = []
sys_includes = ["stdint.h"]
no_includes = true   

Confusingly no_includes means no extra includes. I prefer to have only the ones that I know are needed, rather than some random list of “common” headers.

Here’s my generated header:

#ifndef add_h
#define add_h

/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */

#include <stdint.h>

int32_t add(int32_t a, int32_t b);

#endif /* add_h */

Putting it all together

Example main.c in the root of the crate:

#include <stdio.h>
#include "add.h"

int main() {
    int sum = add(1,2);
    printf("1 + 2 = %d\n", sum);
}

compile and run:

gcc main.c add/target/debug/libadd.a -o app && ./app

outputs: 1 + 2 = 3

This make me unreasonably happy. Rust syntax can be a bit finicky and certainly takes a bit of getting used, but this kind of tooling could more than make up for that in accelerating the dev cycle.

For the full applications with a mini test suite in C, see github/ultrasaurus/rust-clib — the v0.1 branch is from when this blog post was written.