In Pursuit of Laziness

Manish Goregaokar's blog

Mentally Modelling Modules

Posted by Manish Goregaokar on May 14, 2017 in programming, rust, tidbits

Note: This post was written before the Rust 2018 edition, and does not yet incorporate the changes made to the module system.

The module and import system in Rust is sadly one of the many confusing things you have to deal with whilst learning the language. A lot of these confusions stem from a misunderstanding of how it works. In explaining this I’ve seen that it’s usually a common set of misunderstandings.

In the spirit of “You’re doing it wrong”, I want to try and explain one “right” way of looking at it. You can go pretty far1 without knowing this, but it’s useful and helps avoid confusion.

First off, just to get this out of the way, mod foo; is basically a way of saying “look for or foo/ and make a module named foo with its contents”. It’s the same as mod foo { ... } except the contents are in a different file. This itself can be confusing at first, but it’s not what I wish to focus on here. The Rust book explains this more in the chapter on modules.

In the examples here I will just be using mod foo { ... } since multi-file examples are annoying, but keep in mind that the stuff here applies equally to multi-file crates.

Motivating examples

To start off, I’m going to provide some examples of Rust code which compiles. Some of these may be counterintuitive, based on your existing model.

pub mod foo {
    extern crate regex;
    mod bar {
        use foo::regex::Regex;


use std::mem;

pub mod foo {
    // not std::mem::transmute!
    use mem::transmute;

    pub mod bar {
        use foo::transmute;


pub mod foo {
    use bar;
    use bar::bar_inner;

    fn foo() {
        // this works!
        // this doesn't
        // baz::baz_inner();
        // but these do!
        // these do too!

pub mod bar {
    pub fn bar_inner() {}
pub mod baz {
    pub fn baz_inner() {}


pub mod foo {
    use bar::baz;
    // this won't work
    // use baz::inner();
    // this will
    use self::baz::inner;
    // or
    // use bar::baz::inner
    pub fn foo() {
        // but this will work!

pub mod bar {
    pub mod baz {
        pub fn inner() {}


These examples remind me of the “point at infinity” in elliptic curve crypto or fake particles in physics or fake lattice elements in various fields of CS2. Sometimes, for something to make sense, you add in things that don’t normally exist. Similarly, these examples may contain code which is not traditional Rust style, but the import system still makes more sense when you include them.


The core confusion behind how imports work can really be resolved by remembering two rules:

  • use foo::bar::baz resolves foo relative to the root module ( or
    • You can resolve relative to the current module by explicily trying use self::foo::bar::baz
  • foo::bar::baz within your code3 resolves foo relative to the current module
    • You can resolve relative to the root by explicitly using ::foo::bar::baz

That’s actually … it. There are no further caveats. The rest of this is modelling what constitutes as “being within a module”.

Let’s take a pretty standard setup, where extern crate declarations are placed in the the root module:

extern crate regex;

mod foo {
    use regex::Regex;

    fn foo() {
        // won't work
        // let ex = regex::Regex::new("");
        let ex = Regex::new("");

When we say extern crate regex, we pull in the regex crate into the crate root. This behaves pretty similar to mod regex { /* contents of regex crate */}. Basically, we’ve imported the crate into the crate root, and since all use paths are relative to the crate root, use regex::Regex works fine inside the module.

Inline in code, regex::Regex won’t work because as mentioned before inline paths are relative to the current module. However, you can try ::regex::Regex::new("").

Since we’ve imported regex::Regex in mod foo, that name is now accessible to everything inside the module directly, so the code can just say Regex::new().

The way you can view this is that use blah and extern crate blah create an item named blah “within the module”, which is basically something like a symbolic link, saying “yes this item named blah is actually elsewhere but we’ll pretend it’s within the module”

The error message from this code may further drive this home:

use foo::replace;

pub mod foo {
    use std::mem::replace;


The error I get is

error: function `replace` is private
 --> src/
3 | use foo::replace;
  |     ^^^^^^^^^^^^

There’s no function named replace in the module foo! But the compiler seems to think there is?

That’s because use std::mem::replace basically is equivalent to there being something like:

pub mod foo {
    fn replace(...) -> ... {

    // here we can refer to `replace` freely (in inline paths)
    fn whatever() {
        // ...
        let something = replace(blah);
        // ...

except it’s actually like a symlink to the function defined in std::mem. Because inline paths are relative to the current module, saying use std::mem::replace works as if you had defined a function replace in the same module, and you can refer to replace() without needing any extra qualification in inline paths.

This also makes pub use fit perfectly in our model. pub use says “make this symlink, but let others see it too”:

// works now!
use foo::replace;

pub mod foo {
    pub use std::mem::replace;

Folks often get annoyed when this doesn’t work:

mod foo {
    use std::mem;
    // nope
    // use mem::replace;

As mentioned before, use paths are relative to the root module. There is no mem in the root module, so this won’t work. We can make it work via self, which I mentioned before:

mod foo {
    use std::mem;
    // yep!
    use self::mem::replace;

Note that this brings overloading of the self keyword up to a grand total of four! Two cases which occur in the import/path system:

  • use self::foo means “find me foo within the current module”
  • use foo::bar::{self, baz} is equivalent to use foo::bar; use foo::bar::baz;
  • fn foo(&self) lets you define methods and specify if the receiver is by-move, borrowed, mutably borrowed, or other
  • Self within implementations lets you refer to the type being implemented on

Oh well, at least it’s not static.

Going back to one of the examples I gave at the beginning:

use std::mem;

pub mod foo {
    use mem::transmute;

    pub mod bar {
        use foo::transmute;


It should be clearer now why this works. The root module imports mem. Now, from everyone’s point of view, there’s an item called mem in the root.

Within mod foo, use mem::transmute works because use is relative to the root, and mem already exists in the root! When you use something, all child modules will see it as if it were actually belonging to the module. (Non-child modules won’t see it because of privacy, we saw an example of this already)

This is why use foo::transmute works from mod bar, too. bar can refer to the contents of foo via use foo::whatever, since foo is a child of the root module, and use is relative to the root. foo already has an item named transmute inside it because it imported one. Nothing in the parent module is private from the child, so we can use foo::transmute from bar.

Generally, the standard way of doing things is to either not use modules (just a single, or, if you do use modules, put nothing other than extern crates and mods in the root. This is why we rarely see shenanigans like the above; there’s nothing in the root crate to import, aside from other crates specified by extern crate. The trick of “reimport something from the parent module” is also pretty rare because there’s basically no point to using that (just import it directly!). So this is not the kind of code you’ll see in the wild.

Basically, the way the import system works can be summed up as:

  • extern crate and use will act as if they were defining the imported item in the current module, like a symbolic link
  • use foo::bar::baz resolves the path relative to the root module
  • foo::bar::baz in an inline path (i.e. not in a use) will resolve relative to the current module
  • ::foo::bar::baz will always resolve relative to the root module
  • self::foo::bar::baz will always resolve relative to the current module
  • super::foo::bar::baz will always resolve relative to the parent module

Alright, on to the other half of this. Privacy.


So how does privacy work?

Privacy, too, follows some basic rules:

  • If you can access a module, you can access all of its pub contents
  • A module can always access its child modules, but not recursively
    • This means that a module cannot access private items in its children, nor can it access private grandchildren modules
  • A child can always access its parent modules (and their parents), and all their contents
  • pub(restricted) is a proposal which extends this a bit, but it’s experimental so we won’t deal with it here

Giving some examples,

mod foo {
    mod bar {
        // can access `foo::foofunc`, even though `foofunc` is private

        pub fn barfunc() {}

    // can access `foo::bar::barfunc()`, even though `bar` is private
    fn foofunc() {}
mod foo {
    mod bar {
        // We can access our parent and _all_ its contents,
        // so we have access to `foo::baz`. We can access
        // all pub contents of modules we have access to, so we
        // can access `foo::baz::bazfunc`
        use foo::baz::bazfunc;
    mod baz {
        pub fn bazfunc() {}

It’s important to note that this is all contextual; whether or not a particular path works is a function of where you are. For example, this works4:

pub mod foo {
    /* not pub */ mod bar {
        pub mod baz {
            pub fn bazfunc() {}
        pub mod quux {
            use foo::bar::baz::bazfunc;

We are able to write the path foo::bar::baz::bazfunc even though bar is private!

This is because we still have access to the module bar, by being a descendent module.

Hopefully this is helpful to some of you. I’m not really sure how this can fit into the official docs, but if you have ideas, feel free to adapt it5!

  1. This is because most of these misunderstandings lead to a model where you think fewer things compile, which is fine as long as it isn’t too restrictive. Having a mental model where you feel more things will compile than actually do is what leads to frustration; the opposite can just be restrictive. 

  2. One example closer to home is how Rust does lifetime resolution. Lifetimes form a lattice with 'static being the bottom element. There is no top element for lifetimes in Rust syntax, but internally there is the “empty lifetime” which is used during borrow checking. If something resolves to have an empty lifetime, it can’t exist, so we get a lifetime error. 

  3. When I say “within your code”, I mean “anywhere but a use statement”. I may also term these as “inline paths”. 

  4. Example adapted from this discussion 

  5. Contact me if you have licensing issues; I still have to figure out the licensing situation for the blog, but am more than happy to grant exceptions for content being uplifted into official or semi-official docs. 

Two Interpretations Diverged in a Yellow Wood

Posted by Manish Goregaokar on May 02, 2017 in poetry

Whose words are these I think I know
His house is in the village though
He will not see me stopping here
To interpret his work as I go

My little student must think it queer
To read without some context near
Between the words and the intent
He wonders what the poem meant

He gives his head a little shake
To ask if there is some mistake
“That’s not what the author said!”
Providing another view instead

The words are lovely, dark, and deep
But I have literary criticism to preach
And miles to go before I sleep
And miles to go before I sleep

Seriously though, try reading The Road Not Taken as metacircular commentary on how the poem is very often “mis”interpreted, and the nature of interpretation / Death of the Author. It fits perfectly when you read “road” as “interpretation”.

(Yes, I know, the parody above is not based on The Road Not Taken but instead a different Frost poem. I was originally going to modify The Road Not Taken but realized all I had to do was change a few words to get there, which was no fun at all)

Prolonging Temporaries in Rust

Posted by Manish Goregaokar on April 13, 2017 in programming, rust

A colleague of mine learning Rust had an interesting type / borrow checker error. The solution needs a less-used feature of Rust (which basically exists precisely for this kind of thing), so I thought I’d document it.

The code was like this:

let maybe_foo = if some_condition {
    thing.get_ref() // returns Option<&Foo>, borrowed from `thing`
} else {
    thing.get_owned() // returns Option<Foo>


If you want to follow along, here is a full program that does this (playpen):

struct Foo;

struct Thingy {
    foo: Foo

impl Thingy {
    pub fn get_ref(&self) -> Option<&Foo> {
    pub fn get_owned(&self) -> Option<Foo> {
    pub fn new() -> Self {
        Thingy {
            foo: Foo

pub fn main() {
    let some_condition = true;
    let thing = Thingy::new();

    let maybe_foo = if some_condition {
        thing.get_ref() // returns Option<&Foo>, borrowed from `thing`
    } else {
        thing.get_owned() // returns Option<Foo>
    println!("{:?}", maybe_foo);

I’m only going to be changing the contents of main() here.

What’s happening here is that a non-Copy type, Foo, is returned in an Option. In one case, we have a reference to the Foo, and in another case an owned copy.

We want to set a variable to these, but of course we can’t because they’re different types.

In one case, we have an owned Foo, and we can usually obtain a borrow from an owned type. For Option, there’s a convenience method .as_ref() that does this1. Let’s try using that (playpen):

let maybe_foo = if some_condition {
} else {

This will give us an error.

error: borrowed value does not live long enough
  --> <anon>:32:5
31 |         thing.get_owned().as_ref()
   |         ----------------- temporary value created here
32 |     };
   |     ^ temporary value dropped here while still borrowed
35 | }
   | - temporary value needs to live until here

error: aborting due to previous error

The problem is, thing.get_owned() returns an owned value. There’s nothing that it gets anchored to (we don’t set its value to a variable), so it is just a temporary – we can call methods on it, but once we’re done the value will go out of scope.

What we want is something like

let maybe_foo = if some_condition {
} else {
    let owned = thing.get_owned();

but this will still give a borrow error – owned will still go out of scope within the if block, and we need the reference to it last as long as maybe_foo (outside the block) is supposed to last.

So this is no good.

An alternate solution here can be copying/cloning the Foo in the first case by calling .map(|x| x.clone()) or .cloned() or something. Sometimes you don’t want to clone, so this isn’t great.

Another solution here – the generic advice for dealing with values which may be owned or borrow – is to use Cow. It does incur a runtime check, though; one which can be optimized out if things are inlined enough.

What we need to do here is to extend the lifetime of the temporary returned by thing.get_owned(). We need to extend it past the scope of the if.

One way to do this is to have an Option outside that scope which we mutate (playpen).

let mut owned = None;
let maybe_foo = if some_condition {
} else {
    owned = thing.get_owned();

This works in this case, but in this case we already had an Option. If get_ref() and get_owned() returned &Foo and Foo respectively, then we’d need to do something like:

let mut owned = None;
let maybe_foo = if some_condition {
} else {
    owned = Some(thing.get_owned());

which is icky since it introduces an unwrap.

What we really need is a way to signal to the compiler that it needs to hold on to that temporary for the scope of the enclosing block.

We can do that! (playpen)

let owned; // 😯😯😯😯😯
let maybe_foo = if some_condition {
} else {
    owned = thing.get_owned();

We know that Rust doesn’t do “uninitialized” variables. If you want to name a variable, you have to initialize it. let foo; feels rather like magic in this context, because it looks like we’ve declared an uninitialized variable.

What’s less well known is that Rust can do “deferred” initialization. Here, you declare a variable and can initialize it later, but expressions involving the variable can only exist in branches where the compiler knows it has been initialized.

This is the case here. We declared the owned variable beforehand. It now lives in the outer scope and won’t be destroyed until the end of the outer scope. However, the variable cannot be used directly in an expression in the first branch, or after the if. Doing so will give a compile time error saying use of possibly uninitialized variable: `owned`. We can only use it in the else branch because the compiler can see that it is unconditionally initialized in that branch.

We can still read the value of owned indirectly through maybe_foo from outside the branch. This is okay because the storage of owned is guaranteed to live as long as the outer scope, and maybe_foo borrows from it. The only time maybe_foo is set to a value inside owned is when owned has been initialized, so it is safe.

  1. In my experience .as_ref() is the solution to many, many borrow check issues newcomers come across, especially those involving .map() 

You're Doing It Wrong

Posted by Manish Goregaokar on April 05, 2017 in programming

“You’re doing it wrong”

A common refrain in issue trackers and discussion forums everywhere. In isolation, it’s a variant of RTFM – give a non-answer when someone wants help, and bounce them back to a manual or docs which they probably have already read. Not very helpful, and not useful to anyone. Of course, one can accompany it with a nice explanation of how to do it right; “You’re doing it wrong” isn’t always a bad thing :)

Especially when it comes to programming languages, but in general in the context of any programming tool or library, “you’re doing it wrong” is almost always due to a “bad” mental model. The person, whilst learning, has built a mental model of how the tool works, but this doesn’t accurately reflect reality. Other times, it does reflect reality, but it does not reflect the mental model of the maintainers (there can be multiple valid ways of looking at something!), which leads to an impedance mismatch when reading docs or error messages.

In other cases, “doing it wrong” is a case of the XY problem, where the user has problem X, and think they can solve it with solution Y, and end up asking how they can achieve Y. This happens pretty often — folks may be approaching your technology with prior experience with related things that work differently, and may think the same idioms apply.

When I was at WONTFIX, someone who had done support work in the past mentioned that one thing everyone learns in support is “the user is always wrong …. and it’s not their fault!”.

This is a pretty good template for an attitude to approach “doing it wrong” questions about your technology on online forums as well. And this doesn’t just benefit the users who ask questions, this attitude can benefit your technology!

Back when I used to be more active contributing to the Rust compiler, I also used to hang out in #rust a lot, and often answer newbie questions (now #rust-beginners exists too, and I hang out in both, but I don’t really actively participate as much). One thing I learned to do was probe deeper into why people hit that confusion in the first place. It’s almost always a “bad” mental model. Rust is rarely the first programming language folks learn, and people approach it with preconceptions about how programming works. This isn’t unique to Rust, this happens any time someone learns a language with a different paradigm — learning C or C++ after doing a GCd language, learning a functional language after an imperative one, statically typed after dynamic, or one of the many other axes by which programming languages differ.

Other times, it’s just assumptions they made when reading between the lines of whatever resource they used to learn the language.

So, anyway, folks often have a “bad” mental model. If we are able to identify that model and correct it, we have saved that person from potentially getting confused at every step in the future. Great!

With a tiny bit more effort, however, we can do one step better. Not for that person, but for ourselves! We can probe a bit more and try to understand what caused them to obtain that mental model. And fix the docs so that it never happens again! Of course, not everyone reads the docs, but that’s what diagnostics are for (in the case of errors). They’re a tool to help us nudge the user towards the right mental model, whilst helping them fix their immediate problem. Rust has for a long time had pretty great diagnostics, with improvements happening all the time1. I think this is at least in part due to the attitude of the folks in #rust; always trying to figure out how to preempt various confusions they see.

It’s a good attitude to have. I hope more folks, both in and out of the Rust community, approach “You’re doing it wrong” cases like that.

  1. Diagnostics issues are often the easiest way to contribute to the compiler itself, so if you want to contribute, I suggest starting there. Willing to mentor! 

I Never Hear the Phrase 'INHTPAMA' Anymore

Posted by Manish Goregaokar on March 18, 2017 in programming, rust, tidbits

Imagine never hearing the phrase ‘INHTPAMA’ again.

Oh, that’s already the case? Bummer.

Often, when talking about Rust, folks refer to the core aliasing rule as “that &mut thing”, “compile-time RWLock” (or “compile-time RefCell”), or something similar. Basically, referring to the fact that you can’t mutate the data that is currently held via an & reference, and that you can’t mutate or read the data currently held via an &mut reference except through that reference itself.

It’s always bugged me that we really don’t have a name for this thing. It’s one of the core bits of Rust, and crops up often in discussions.

But we did have a name for it! It was “INHTPAMA” (which was later butchered into “INHTWAMA”).

This is a reference to Niko’s 2012 blog post, titled “Imagine Never Hearing The Phrase ‘aliasable, mutable’ again”. It’s where the aliasing rules came from. Go read it, it’s great. It talks about this weird language with at symbols and purity, but I assure you, that language is Baby Rust. Or maybe Teenage Rust. The lifecycle of rusts is complex and interesting and I don’t know how to categorize it.

The point of this post isn’t really to encourage reviving the use of “INHTWAMA”; it’s a rather weird acronym that will probably confuse folks. I would like to have a better way of refering to “that &mut thing”, but I’d prefer if it wasn’t a confusing acronym that carries no meaning of its own if you don’t know the history of it. That’s a recipe for making new community members feel like outsiders.

But that post is amazing and I’d hate to see it drop out of the collective memory of the Rust community.

Use Signal. Use Tor.

Posted by Manish Goregaokar on March 12, 2017 in cryptography, poetry, programming

I went to send a missive today
As I have done so oft before
But I forgot to employ that scrap of advice
“Use Signal. Use Tor.”

Intercepted of course the missive was
By a ferocious beast of lore
Because I failed to use that bit of advice
“Use Signal. Use Tor.”

The beast was strong; and formidable
He hated the amendments four
I should have remembered that piece of advice
“Use Signal. Use Tor.”

I tried to reason with the beast
but he only wanted war
Do not neglect that important advice
“Use Signal. Use Tor.”

Here I lie in the belly of the beast
I shall discount this advice no more
If I ever manage to leave this place
I’ll use Signal, and Tor.

Heed this advice, children.
It’s not something to ignore
Always, always, always, always
Use Signal. Use Tor.

Why Quantum Computing Is Weird

Posted by Manish Goregaokar on March 11, 2017 in physics

I’ve been meaning to write about physics for a while. When I started this blog the intention was to write about a wide variety of interests, but I ended up focusing on programming, despite the fact that I was doing more physics than programming for most of the lifetime of this blog. Time to change that, and hopefully write about other non-programming topics too.

Quantum Computing. It’s the new hip thing that’s going to change the world1. Someday.

In it’s essence, where classical computing deals with “bits”, which are on/off states, quantum computing deals with “qubits”, which are probabalistic quantum states that are often a mixture of on and off. These have interesting properties which make certain kinds of so-far-hard computation very easy to perform.

The goal of this post is not to teach quantum computing, rather to garner interest. I come to praise quantum computing, not bury it2. As a result, this post doesn’t require a background in physics. Having worked with very simple logic circuits is probably enough, though you may not even need that.

I’m basically going to sketch out an example of a very simple quantum algorithm. One that’s very logic-defying. It’s even logic-defying for many who have studied quantum mechanics; it certainly was for me. When I learned this first I could understand why it worked but there was a lot of dissonance between that and my intuitive conviction that it was wrong.

The algorithm

This is a quantum circuit (specifically, the circuit for the Deutsch-Jozsa algorithm). It’s used to find out the nature of a black-box function f(x), which takes in one qubit and outputs another3. For now, you can try to interpret this circuit as if it were a regular logic circuit. You’ll soon see that this interpretation is wrong, but it’s useful for the purposes of this explanation.

To run this algorithm, you first construct an “oracle” out of the black-box function. The oracle, given inputs x and y, has outputs x and y ⊕ f(x) (where is the symbol for XOR, the “exclusive OR”).

As with logic circuits, data flow here goes from left to right. This circuit has two constant inputs, a zero and a one. This is similar to how we might have constant “true” and “false” inputs to logic circuits.

They are then passed through “Hadamard gates”. These are like NOT gates, in that applying them twice is a no-op (they are their own inverse), but they’re not actually NOT gates. I like to describe them as “sideways NOT gates” since that description somewhat intuitively captures what’s going on with the qubits. What’s important to note here is that they have one input and one output, so they’re unaffected by the goings-on in a different wire.

Once these inputs have been Hadamard’ed, they are fed to the oracle we constructed. The top input goes on to become the top output. It’s also passed through f(x) and XORd with the bottom input to make the bottom output.

The top output is then Hadamard’ed again, and finally we observe its value.

Here’s where the magic comes in. By observing the top output, we will know the nature of f(x)4.

Wait, what? The top output doesn’t appear to have any interaction with f(x) at all! How can that work?

In fact, we could try to rewrite this circuit such that the measured output definitely has no interaction with f(x) whatever, assuming that the Hadamard gate isn’t doing anything funky5 (it isn’t):

How in the world does this work?

Why it works

Sadly, I can’t give a satisfying explanation to exactly why this works. This requires some quantum mechanics background6 to grasp.

However, I can give a hopefully-satisfying explanation as to why our regular intuition doesn’t work here.

First and foremost: The rewritten circuit I showed above? It’s wrong. If this was a logic circuit, we could always do that, but in quantum computing, T-junctions like the following can’t exist:

This is due to the “No Cloning theorem”. Unlike regular logic circuits, you can’t just “duplicate” a qubit. In some cases (like this one), you can try to create a similar qubit via the same process (e.g. here we could take another 0 and pass it through a Hadamard gate), but it’s not the “same” qubit. Unlike bits, qubits have a stronger notion of unique identity.

And it’s this sense of identity that fuels this algorithm (and most of quantum computing).

You see, while the top output of the oracle was x, it wasn’t exactly the same x. This x had been mixed with the lower output. This means that the upper and lower outputs are now entangled, with their state depending on each other. In fact, it’s really misleading to show the output as two wires in the first place – it’s really a single “entangled” state of two qubits that can’t be decomposed as a “top half” and a “bottom half”. Of course, this way of representing quantum circuits is still used because it’s a tidy way of visualizing these circuits, and physicists are aware of the caveats involved.

So what happens is that when you observe the top output, you are really doing a partial observation on the combined state of the two outputs, and this includes some information about f(x), which leaks out when you perform the observation.

These properties of qubits make quantum circuits work significantly differently from regular logic ones. On one hand, this severely restricts what you can do with them, but at the same time, new avenues of erstwhile-impossible operations open up. Most useful quantum algorithms (like Shor’s factorization algorithm) involve a mixture of a classical algorithm and a quantum circuit due to this reason. It’s pretty cool!

  1. What isn’t? 

  2. The abstruseness of physics lives after it; the coolness is oft interred with its bones. 

  3. This actually can be generalized to a function with n input and n output qubits, and the circuit stays mostly the same, except the top “x” line becomes n lines all initialized to 0 and passing through n parallel H gates. 

  4. Specifically, if the observation is 1, the function is a constant, whereas if the observation is 0, the function is “balanced” (gives a different output for inputs 1 and 0) 

  5. For Hadamard is an honorable gate. So are they all, all honorable gates. 

  6. If you do have this background, it’s relatively straightforward; the Wikipedia page has the equations for it. 

Understanding Git Filter-branch and the Git Storage Model

Posted by Manish Goregaokar on March 05, 2017 in programming

The other day Steve wanted git alchemy done on the Rust repo.

Specifically, he wanted the reference and nomicon moved out into their own repositories, preserving history. Both situations had some interesting quirks, the reference has lived in src/doc/reference/* and src/doc/, and the nomicon has lived in src/doc/nomicon, src/doc/tarpl, and at the top level in a separate git root.

As you can guess from the title of this post, the correct tool for this job is git filter-branch. My colleague Greg calls it “the swiss-army knife of Git history rewriting”.

I had some fun with filter-branch that day, thought I’d finally write an accessible tutorial for it. A lot of folks treat filter-branch like rebase, but it isn’t, and this crucial difference can lead to many false starts. It certainly did for me back when I first learned it.

This kind of ties into the common bit of pedantry about the nature of a commit I keep seeing pop up:

Git commits appear to be diffs, but they’re actually file copies, but they’re actually ACTUALLY diffs.

So what is a git commit?

Generally we interact with git commits via git show or by looking at commits on a git GUI / web UI. Here, we see diffs. It’s natural to think of a commit as a diff, it’s the model that makes the most sense for the most common ways of interacting with commits. It also makes some sense from an implementation point of view, diffs seem like an efficient way of storing things.

It turns out that the “real” model is not this, it’s actually that each commit is a snapshot of the whole repo state at the time.

But actually, it isn’t, the underlying implementation does make use of deltas in packfiles and some other tricks like copy-on-write forking.

Ultimately, arguing about the “real” mental model is mostly pedantry. There are multiple ways of looking at a commit. The documentation tends to implicitly think of them as “full copies of the entire file tree”, which is where most of the confusion about filter-branch comes from. But often it’s important to picture them as diffs, too.

Understanding the implementation can be helpful, especially when you break the repository whilst doing crazy things (I do this often). I’ve explained how it works in a later section, it’s not really a prerequisite for understanding filter-branch, but it’s interesting.

How do I rewrite history with git rebase?

This is where some of the confusion around filter-branch stems from. Folks have worked with rebase, and they think filter-branch is a generalized version of this. They’re actually quite different.

For those of you who haven’t worked with git rebase, it’s a pretty useful way of rewriting history, and is probably what you should use when you want to rewrite history, especially for maintaining clean git history in an unmerged under-review branch.

Rebase does a whole bunch of things. Its core task is, given the current branch and a branch that you want to “rebase onto”, it will take all commits unique to your branch, and apply them in order to the new one. Here, “apply” means “apply the diff of the commit, attempting to resolve any conflicts”. At times, it may ask you to manually resolve the conflicts, using the same tooling you use for conflicts during git merge.

Rebase is much more powerful than that, though. git rebase -i will open up “interactive rebase”, which will show you the commits that are going to be rebased. In this interface, you can reorder commits, mark them for edits (wherein the rebase will stop at that commit and let you git commit --amend changes into it), and even “squash” commits which lets you mark a commit to be absorbed into the previous one. This is rather useful for when you’re working on a feature and want to keep your commits neat, but also want to make fixup patches to older commits. Filippo’s git fixup alias packages this particular task into a single git command. Changing EDITOR=true into EDITOR=: GIT_SEQUENCE_EDITOR=: will make it not even open the editor for confirmation and try to do the whole thing automatically.

git rebase -x some_command is also pretty neat, lets you run a shell command on each step during a rebase.

In this model, you are fundamentally thinking of commits as diffs. When you move around commits in the interactive rebase editor, you’re moving around diffs. When you mark things for squashing, you’re basically merging diffs. The whole process is about taking a set of diffs and applying them to a different “base commit”.

How do I rewrite history with git filter-branch?

filter-branch does not work with diffs. You’re working with the “snapshot” model of commits here, where each commit is a snapshot of the tree, and rewriting these commits.

What git filter-branch will do is for each commit in the specified branch, apply filters to the snapshot, and create a new commit. The new commit’s parent will be the filtered version of the old commit’s parent. So it creates a parallel commit DAG.

Because the filters apply on the snapshots instead of the diffs, there’s no chance for this to cause conflicts like in git rebase. In git rebase, if I have one commit that makes changes to a file, and I change the previous commit to just remove the area of the file that was changed, I’d have a conflict and git would ask me to figure out how the changes are supposed to be applied.

In git-filter-branch, if I do this, it will just power through. Unless you explicitly write your filters to refer to previous commits, the new commit is created in isolation, so it doesn’t worry about changes to the previous commits. If you had indeed edited the previous commit, the new commit will appear to undo those changes and apply its own on top of that.

filter-branch is generally for operations you want to apply pervasively to a repository. If you just want to tweak a few commits, it won’t work, since future commits will appear to undo your changes. git rebase is for when you want to tweak a few commits.

So, how do you use it?

The basic syntax is git filter-branch <filters> branch_name. You can use HEAD or @ to refer to the current branch instead of explicitly typing branch_name.

A very simple and useful filter is the subdirectory filter. It makes a given subdirectory the repository root. You use it via git filter-branch --subdirectory-filter name_of_subdir @. This is useful for extracting the history of a folder into its own repository.

Another useful filter is the tree filter, you can use it to do things like moving around, creating, or removing files. For example, if you want to move to README in the entire history, you’d do something like git filter-branch --tree-filter 'mv README' @ (you can also achieve this much faster with some manual work and rebase). The tree filter will work by checking out each commit (in a separate temporary folder), running your filter on the working directory, adding any changes to the index (no need to git add yourself), and committing the new index.

The --prune-empty argument is useful here, as it removes commits which are now empty due to the rewrite.

Because it is checking out each commit, this filter is quite slow. When I initially was trying to do Steve’s task on the rust repo, I wrote a long tree filter and it was taking forever.

The faster version is the index filter. However, this is a bit trickier to work with (which is why I tend to use a tree filter if I can get away with it). What this does is operate on the index, directly.

The “index” is basically where things go when you git add them. Running git add will create temporary objects for the added file, and modify the WIP index (directory tree) to include a reference to the new file or change an existing file reference to the new one. When you commit, this index is packaged up into a commit and stored as an object. (More on how these objects work in a later section)

Now, since this deals with files that are already stored as objects, git doesn’t need to unwrap these objects and create a working directory to operate on them. So, with --index-filter, you can operate on these in a much faster way. However, since you don’t have a working directory, stuff like adding and moving files can be trickier. You often have to use git update-index to make this work.

However, a useful index filter is one which just scrubs a file (or files) from history:

$ git filter-branch --index-filter 'git rm --cached --ignore-unmatch filename' HEAD

The --ignore-unmatch makes the command still succeed if the file doesn’t exist. filter-branch will fail if one of the filters fails. In general I tend to write fallible filters like command1 1>&2 2>/dev/null ; command2 1>&2 2>/dev/null ; true, which makes it always succeed and also ignores any stdout/stderr output (which tends to make the progress screen fill up fast).

The --cached argument on git rm makes it operate only on the index, not the working directory. This is great, because we don’t have a working directory right now.

I rarely use git update-index so I’m not really going to try and explain how it can be used here. But if you need to do more complex operations in an index filter, that’s the way to go.

There are many other filters, like --commit-filter (lets you discard a commit entirely), --msg-filter (rewriting commit messages), and --env-filter (changing things like author metadata or other env vars). You can see a complete list with examples in the docs

How did I perform the rewrites on the reference and nomicon?

For the Rust Reference, basically I had to extract the history of src/doc/, AND src/doc/reference/* ( was split up into reference/*.md recently) into its own commit. This is an easy tree filter to write, but tree filters take forever.

Instead of trying my luck with an index filter, I decided to just make it so that the tree filter would be faster. I first extracted src/doc/:

$ git filter-branch -f --prune-empty --subdirectory-filter src/doc @

Now I had a branch that contained only the history of src/doc, with the root directory moved to doc. This is a much smaller repo than the entirety of Rust.

Now, I moved into reference/:

$ git filter-branch -f --prune-empty --tree-filter 'mkdir -p reference; mv reference 1>/dev/null 2>/dev/null; true' @

As mentioned before, the /dev/null and true bits are because the mv command will fail in some cases (when doesn’t exist), and I want it to just continue without complaining when that happens. I only care about moving instances of that file, if that file doesn’t exist there it’s still okay.

Now, everything I cared about was within reference. The next step was simple:

$ git filter-branch -f --prune-empty --subdirectory-filter reference @

The whole process took maybe 10 minutes to run, most of the time being spent by the second command. The final result can be found here.

For the nomicon, the task was easier. In the case of the nomicon, it has always resided in src/doc/nomicon, src/doc/tarpl, or at the root. This last bit is interesting, when Alexis was working on the nomicon, she started off by hacking on it in a separate repo, but then within that repo moved it to src/doc/tarpl, and performed a merge commit with rustc. There’s no inherent restriction in Git that all merges must have a common ancestor, and you can do stuff like this. I was quite surprised when I saw this, since it’s pretty uncommon in general, but really, many projects of that size will have stuff like this. Servo and html5ever do too, and usually it’s when a large project is merged into it after being developed on the side.

This sounds complicated to work with, but it wasn’t that hard. I took the same subdirectory-filtere’d doc directory branch used for the reference. Then, I renamed tarpl/ to nomicon/ via a tree filter, and ran another subdirectory filter:

$ git filter-branch -f --prune-empty --tree-filter 'mv tarpl nomicon 1>/dev/null 2>/dev/null; true' @
$ git filter-branch -f --prune-empty --subdirectory-filter nomicon @

Now, I had the whole history of the nomicon in the root dir. Except for the commits made by Alexis before her frankenmerge, because these got removed in the first subdirectory filter (the commits were operating outside of src/doc, even though their contents eventually got moved there).

But, at this stage, I already had a branch with the nomicon at the root. Alexis’ original commits were also operating on the root directory. I can just rebase here, and the diffs of my commits will cleanly apply!

I found the commit (a54e64) where everything was moved to tarpl/, and took its parent (c7919f). Then, I just ran git rebase --root c7919f, and everything cleanly rebased. As expected, because I had a history going back to the first child of a54e64 with files moved, and a54e64 itself only moved files, so the diffs should cleanly apply.

The final result can be found here.

Appendix: How are commits actually stored?

The way the actual implementation of a commit works is that each file being stored is hashed and stored in a compressed format, indexed by the hash. A directory (“tree”) will be a list of hashes, one for each file/directory inside it, alongside the filenames and other metadata. This list will be hashed and used everywhere else to refer to the directory.

A commit will reference the “tree” object for the root directory via its hash.

Now, if you make a commit changing some files, most of the files will be unchanged. So will most of the directories. So the commits can share the objects for the unchanged files/directories, reducing their size. This is basically a copy-on-write model. Furthermore, there’s a second optimization called a “packfile”, wherein instead of storing a file git will store a delta (a diff) and a reference to the file the diff must be applied to.

We can see this at work using git cat-file. cat-file lets you view objects in the “git filesystem”, which is basically a bunch of hash-indexed objects stored in .git/objects. You can view them directly by traversing that directory (they’re organized as a trie), but cat-file -p will let you pretty-print their contents since they’re stored in a binary format.

I’m working with the repo for the Rust Book, playing with commit 4822f2. It’s a commit that changes just one file (second-edition/src/ ), perfect.

$ git show 4822f2baa69c849e4fa3b85204f219a16bde2f42
commit 4822f2baa69c849e4fa3b85204f219a16bde2f42
Author: Jake Goulding <...>
Date:   Fri Mar 3 14:07:24 2017 -0500

    Reorder sentence about a generic cons list.

diff --git a/second-edition/src/ b/second-edition/src/
index 14c5533..29d8793 100644
--- a/second-edition/src/
+++ b/second-edition/src/
(diff omitted)

$ git cat-file -p 4822f2baa69c849e4fa3b85204f219a16bde2f42

tree ec7cd2821d4bcbafe08f3eca6ea60487bfdc1b52
parent 24cd100e061bb11c3f7f3219467d6d644c50d811
author Jake Goulding <...> 1488568044 -0500
committer GitHub <> 1488568044 -0500

Reorder sentence about a generic cons list.

This tells us that the commit is a thing with some author information, a pointer to a parent, a commit message, and a “tree”. What’s this tree?

$ git cat-file -p ec7cd2821d4bcbafe08f3eca6ea60487bfdc1b52
100644 blob 4cab1f4d267628ab5f4f7c14b1b64a9d4b032409    .gitattributes
040000 tree e1dcc1c754d72450b03542b2106fcb67c78805ff    .github
100644 blob 4c699f440ac134c577cb6f67b04ec5b93c652440    .gitignore
100644 blob e86d887d84a839417c960faf877c9057a8dc6823    .travis.yml
100644 blob 7990f2738876fc0fbc2ca30f5f91e91745b0b8eb
040000 tree 17b33cb52a5abb67ff678a03e7ed88cf9f163c69    ci
040000 tree 0ffd2c1238345c1b0e99af6c1c618eee4a0bab58    first-edition
100644 blob 5d1d2bb79e1521b28dd1b8ff67f9b04f38d83620
040000 tree b7160f7d05d5b5bfe28bad029b1b490e310cff22    redirects
040000 tree d5672dd9ef15adcd1527813df757847d745e299a    second-edition

This is just a directory! You can see that each entry has a hash. We can use git cat-file -p to view each one. Looking at a tree object will just give us a subdirectory, but the blobs will show us actual files!

$ git cat-file -p 7990f2738876fc0fbc2ca30f5f91e91745b0b8eb # Show README
# The Rust Programming Language

[![Build Status](](

To read this book online, visit [][html].

(rest of file omitted)

So how does this share objects? Let’s look at the previous commit:

$ git cat-file -p 4822f2baa69c849e4fa3b85204f219a16bde2f42^ # `^` means "parent"
tree d219be3c5010f64960ddb609a849fc42a01ad31b
parent 21c063868f9d7fb0fa488b6f1124262f055d275b
author steveklabnik <...> 1488567224 -0500
committer steveklabnik <...> 1488567239 -0500

mdbook needs to be on the PATH for deploy

$ git cat-file -p d219be3c5010f64960ddb609a849fc42a01ad31b # the tree
100644 blob 4cab1f4d267628ab5f4f7c14b1b64a9d4b032409    .gitattributes
040000 tree e1dcc1c754d72450b03542b2106fcb67c78805ff    .github
100644 blob 4c699f440ac134c577cb6f67b04ec5b93c652440    .gitignore
100644 blob e86d887d84a839417c960faf877c9057a8dc6823    .travis.yml
100644 blob 7990f2738876fc0fbc2ca30f5f91e91745b0b8eb
040000 tree 17b33cb52a5abb67ff678a03e7ed88cf9f163c69    ci
040000 tree 0ffd2c1238345c1b0e99af6c1c618eee4a0bab58    first-edition
100644 blob 5d1d2bb79e1521b28dd1b8ff67f9b04f38d83620
040000 tree b7160f7d05d5b5bfe28bad029b1b490e310cff22    redirects
040000 tree d48b2e06970cf3a6ae65655c340922ae69723989    second-edition

If you look closely, all of these hashes are the same, except for the hash for second-edition. For the hashes which are the same, these objects are being shared across commits. The differing hash is d5672d in the newer commit, and d48b2e in the older one.

Let’s look at the objects:

$ git cat-file -p d5672d
100644 blob 82dc67a6b08f0eb62420e4da3b3aa9c0dc10911a
100644 blob 5cd51aa43f05416996c4ef055df5d6eb58fbe737    Cargo.lock
100644 blob 7ab2575fa5bf4abf6eaf767c72347580c9f769dd    Cargo.toml
100644 blob 96e9f0458b55a4047927de5bf04ceda89d772b2b    LICENSE-APACHE
100644 blob 5a56e6e8ed1909b4e4800aa8d2a0e7033ab4babe    LICENSE-MIT
100644 blob be1135fc6d28eca53959c7fc9ae191523e4bc96f    book.json
100644 blob 1400454f36840e916a7d7028d987c42fcb31b4db    dictionary.txt
100644 blob 5103c84d034d6e8a0e4b6090453ad2cdcde21537
040000 tree 6715d1d4c97e3d17a088922f687b8d9ffacb5953    dot
100644 blob f9e045c4c1824520534270a2643ebe68311503b8
040000 tree f8d9a9452b4bbaeba256b95d40b303cd5fb20a64    nostarch
100644 blob 0a2d16852c11355ef9d8758a304b812633dcf03c
040000 tree 3f8db396566716299330cdd5f569fb0a0c4615dd    src
100644 blob 56677811f451084de7c3a2478587a09486209b14
040000 tree 7601821a2ff38906332082671ea23e4074464dd2    tools

$ git cat-file -p d48b2e
100644 blob 82dc67a6b08f0eb62420e4da3b3aa9c0dc10911a
100644 blob 5cd51aa43f05416996c4ef055df5d6eb58fbe737    Cargo.lock
100644 blob 7ab2575fa5bf4abf6eaf767c72347580c9f769dd    Cargo.toml
100644 blob 96e9f0458b55a4047927de5bf04ceda89d772b2b    LICENSE-APACHE
100644 blob 5a56e6e8ed1909b4e4800aa8d2a0e7033ab4babe    LICENSE-MIT
100644 blob be1135fc6d28eca53959c7fc9ae191523e4bc96f    book.json
100644 blob 1400454f36840e916a7d7028d987c42fcb31b4db    dictionary.txt
100644 blob 5103c84d034d6e8a0e4b6090453ad2cdcde21537
040000 tree 6715d1d4c97e3d17a088922f687b8d9ffacb5953    dot
100644 blob f9e045c4c1824520534270a2643ebe68311503b8
040000 tree f8d9a9452b4bbaeba256b95d40b303cd5fb20a64    nostarch
100644 blob 0a2d16852c11355ef9d8758a304b812633dcf03c
040000 tree f9fc05a6ff78b8211f4df931ed5e32c937aba66c    src
100644 blob 56677811f451084de7c3a2478587a09486209b14
040000 tree 7601821a2ff38906332082671ea23e4074464dd2    tools

Again, these are the same, except for that of src. src has a lot of files in it, which will clutter this post, so I’ll run a diff on the outputs of cat-file:

$ diff -U5 <(g cat-file -p f9fc05a6ff78b8211f4df931ed5e32c937aba66c) <(g cat-file -p 3f8db396566716299330cdd5f569fb0a0c4615dd)
--- /dev/fd/63  2017-03-05 11:58:22.000000000 -0800
+++ /dev/fd/62  2017-03-05 11:58:22.000000000 -0800
@@ -63,11 +63,11 @@
 100644 blob ff6b8f8cd44f624e1239c47edda59560cdf491ae
 100644 blob c53ef854a74b6c9fbd915be1bf824c6e78439c42
 100644 blob 3fb59f9cc85b6b81994e83a34d542871a260a8f0
 100644 blob e1cd1ca779fdf202af433108a8af6eda317f2717
 100644 blob 3173cc508484cc447ebe42a024eac7d9e6c2ddcd
-100644 blob 14c5533bb3b604c6e6274db278d1e7129f78d55d
+100644 blob 29d87933d6832374b87d98aa5588e09e0c1a4991
 100644 blob 47b35ed489d63ce6a885289fec01b7b16ba1afea
 100644 blob 2d20c55cc8605c0c899bc4867adc6b6ea1f5c902
 100644 blob 8e3fcf4e83fe1ce985a7c0b479b8b16701765aaf
 100644 blob a4ade4ae8bf5296d79ed51d69506e71a83f9f489
 100644 blob 3a4db5616c4f5baeb95d04ea40c6747e60181684

As you can see, only the file that was changed in the commit has a new blob stored. If you view 14c553 and 29d879 you’ll get the pre- and post- commit versions of the file respectively.

So basically, each commit stores a tree of references to objects, often sharing nodes with other commits.

I haven’t had the opportunity to work with packfiles much, but they’re an additional optimization on top of this. Aditya’s post is a good intro to these.

What Are Sum, Product, and Pi Types?

Posted by Manish Goregaokar on March 04, 2017 in programming, rust

See also: Tony’s post on the same topic

You often hear people saying “Language X1 has sum types” or “I wish language X had sum types”2, or “Sum types are cool”.

Much like fezzes and bow ties, sum types are indeed cool.

These days, I’ve also seen people asking about “Pi types”, because of this Rust RFC.

But what does “sum type” mean? And why is it called that? And what, in the name of sanity, is a Pi type?

Before I start, I’ll mention that while I will be covering some type theory to explain the names “sum” and “product”, you don’t need to understand these names to use these things! Far too often do people have trouble understanding relatively straightforward concepts in languages because they have confusing names with confusing mathematical backgrounds3.

So what’s a sum type? (the no-type-theory version)

In it’s essence, a sum type is basically an “or” type. Let’s first look at structs.

struct Foo {
    x: bool,
    y: String,

Foo is a bool AND a String. You need one of each to make one. This is an “and” type, or a “product” type (I’ll explain the name later).

So what would an “or” type be? It would be one where the value can be a bool OR a String. You can achieve this with C++ with a union:

union Foo {
    bool x;
    string y;

foo.x = true; // set it to a bool
foo.y = "blah"; // set it to a string

However, this isn’t exactly right, since the value doesn’t store the information of which variant it is. You could store false and the reader wouldn’t know if you had stored an empty string or a false bool.

There’s a pattern called “tagged union” (or “discriminated union”) in C++ which bridges this gap.

union FooUnion {
    bool x;
    string y;

enum FooTag {

struct Foo {
    FooUnion data;
    FooTag tag;

// set it to a bool = true;
foo.tag = BOOL;

// set it to a string = "blah";
foo.tag = STRING;

Here, you manually set the tag when setting the value. C++ also has std::variant (or boost::variant) that encapsulates this pattern with a better API.

While I’m calling these “or” types here, the technical term for such types is “sum” types. Other languages have built-in sum types.

Rust has them and calls them “enums”. These are a more generalized version of the enums you see in other languages.

enum Foo {

let foo = Foo::Bool(true);

// "pattern matching"
match foo {
    Str(s) => /* do something with string `s` */,
    Bool(b) => /* do something with bool `b` */,

Swift is similar, and also calls them enums

enum Foo {
    case str(String)
    case boolean(bool)

let foo = Foo.boolean(true);
switch foo {
    case .str(let s):
        // do something with string `s`
    case .boolean(let b):
        // do something with boolean `b`

You can fake these in Go using interfaces, as well. Typescript has built-in unions which can be typechecked without any special effort, but you need to add a tag (like in C++) to pattern match on them.

Of course, Haskell has them:

data Foo = B Bool | S String

-- define a function
doThing :: Foo -> SomeReturnType
doThing (B b) = -- do something with boolean b
doThing (S s) = -- do something with string s

-- call it
doThing (S "blah")
doThing (B True)

One of the very common things that languages with sum types do is express nullability as a sum type;

// an Option is either "something", containing a type, or "nothing"
enum Option<T> {

let x = Some("hello");
match x {
    Some(s) => println!("{}", s),
    None => println!("no string for you"),

Generally, these languages have “pattern matching”, which is like a switch statement on steroids. It lets you match on and destructure all kinds of things, sum types being one of them. Usually, these are “exhaustive”, which means that you are forced to handle all possible cases. In Rust, if you remove that None branch, the program won’t compile. So you’re forced to deal with the none case, somehow.

In general sum types are a pretty neat and powerful tool. Languages with them built-in tend to make heavy use of them, almost as much as they use structs.

Why do we call it a sum type?

Here be (type theory) dragons

Let’s step back a bit and figure out what a type is.

It’s really a restriction on the values allowed. It can have things like methods and whatnot dangling off it, but that’s not so important here.

In other words, it’s like4 a set. A boolean is the set \(\{\mathtt{true}, \mathtt{false}\}\). An 8-bit unsigned integer (u8 in Rust) is the set \(\{0, 1, 2, 3, …. 254, 255\}\). A string is a set with infinite elements, containing all possible valid strings5.

What’s a struct? A struct with two fields contains every possible combination of elements from the two sets.

struct Foo {
    x: bool,
    y: u8,

The set of possible values of Foo is

\[\{(\mathtt{x}, \mathtt{y}): \mathtt{x} \in \mathtt{bool}, \mathtt y \in \mathtt{u8}\}\]

(Read as “The set of all \((\mathtt{x}, \mathtt{y})\) where \(\tt x\) is in \(\mathtt{bool}\) and \(\tt y\) is in \(\mathtt{u8}\)”)

This is called a Cartesian product, and is often represented as \(\tt Foo = bool \times u8\). An easy way to view this as a product is to count the possible values: The number of possible values of Foo is the number of possible values of bool (2) times the number of possible values of u8 (256).

A general struct would be a “product” of the types of each field, so something like

struct Bar {
    x: bool,
    y: u8,
    z: bool,
    w: String

is \(\mathtt{Bar = bool \times u8 \times bool \times String}\)

This is why structs are called “product types”6.

You can probably guess what comes next – Rust/Swift enums are “sum types”, because they are the sum of the two sets.

enum Foo {

is a set of all values which are valid booleans, and all values which are valid integers. This is a sum of sets, \(\tt Foo = bool + u8\). More accurately, it’s a disjoint union, where if the input sets have overlap, the overlap is “discriminated” out.

An example of this being a disjoint union is:

enum Bar {

This is not \(\tt Bar = bool + bool + u8\), because \(\tt bool + bool = bool\), (regular set addition doesn’t duplicate the overlap).

Instead, it’s something like

\[\tt Bar = bool + otherbool + u8\]

where \(\tt otherbool\) is also a set \(\tt \{true, false\}\), except that these elements are different from those in \(\tt bool\). You can look at it as if

\[\tt otherbool = \{true_2, false_2\}\]

so that

\[\mathtt{bool + otherbool} = \{\mathtt{true, false, true_2, false_2}\}\]

For sum types, the number of possible values is the sum of the number of possible values of each of its component types.

So, Rust/Swift enums are “sum types”.

You may often notice the terminology “algebraic datatypes” (ADT) being used, usually that’s just talking about sum and product types together – a language with ADTs will have both.

In fact, you can even have exponential types! The notation A^B in set theory does mean something, it’s the set of all possible mappings from \(B\) to \(A\). The number of elements is \({N_A}^{N_B}\). So basically, the type of a function (which is a mapping) is an “exponential” type. You can also view it as an iterated product type, a function from type B to A is really a struct like this:

// the type
fn my_func(b: B) -> A;

// is conceptually (each possible my_func can be written as an instance of)

struct my_func {
    b1: A, // value for first element in B
    b2: A, // value for second element in B
    b3: A,
    // ... 

given a value of the input b, the function will find the right field of my_func and return the mapping. Since a struct is a product type, this is

\[\mathtt{A}^{N_\mathtt{B}} = \tt A \times A \times A \times \dots\]

making it an exponential type.

You can even take derivatives of types! (h/t Sam Tobin-Hochstadt for pointing this out to me)

What, in the name of sanity, is a Pi type?

It’s essentially a form of dependent type. A dependent type is when your type can depend on a value. An example of this is integer generics, where you can do things like Array<bool, 5>, or template<unsigned int N, typename T> Array<T, N> ... (in C++).

Note that the type signature contains a type dependent on an integer, being generic over multiple different array lengths.

The name comes from how a constructor for these types would look:

// create an array of booleans from a given integer
// I made up this syntax, this is _not_ from the Rust Pi type RFC
fn make_array(x: u8) -> Array<bool, x> {
    // ...

// or
// (the proposed rust syntax)
fn make_array<const x: u8>() -> Array<bool, x> {
   // ... 

What’s the type of make_array here? It’s a function which can accept any integer and return a different type in each case. You can view it as a set of functions, where each function corresponds to a different integer input. It’s basically:

struct make_array {
    make_array_0: fn() -> Array<bool, 0>,
    make_array_1: fn() -> Array<bool, 1>,
    make_array_2: fn() -> Array<bool, 2>,
    make_array_3: fn() -> Array<bool, 3>,
    make_array_4: fn() -> Array<bool, 4>,
    make_array_5: fn() -> Array<bool, 5>,
    // ... 

Given an input, the function chooses the right child function here, and calls it.

This is a struct, or a product type! But it’s a product of an infinite number of types7.

We can look at it as

\[\texttt{make_array} = \prod\limits_{x = 0}^\infty\left( \texttt{fn()} \mathtt\to \texttt{Array<bool, x>}\right)\]

The usage of the \(\Pi\) symbol to denote an iterative product gives this the name “Pi type”.

In languages with lazy evaluation (like Haskell), there is no difference between having a function that can give you a value, and actually having the value. So, the type of make_array is the type of Array<bool, N> itself in languages with lazy evaluation.

There’s also a notion of a “sigma” type, which is basically

\[\sum\limits_{x = 0}^\infty \left(\texttt{fn()} \mathtt\to \texttt{Array<bool, x>}\right)\]

With the Pi type, we had “for all N we can construct an array”, with the sigma type we have “there exists some N for which we can construct this array”. As you can expect, this type can be expressed with a possibly-infinite enum, and instances of this type are basically instances of Array<bool, N> for some specific N where the N is only known at runtime. (much like how regular sum types are instances of one amongst multiple types, where the exact type is only known at runtime). Vec<bool> is conceptually similar to the sigma type Array<bool, ?>, as is &[bool].

Wrapping up

Types are sets, and we can do set-theory things on them to make cooler types.

Let’s try to avoid using confusing terminology, however. If Rust does get “pi types”, let’s just call them “dependent types” or “const generics” :)

Thanks to Zaki, Avi Weinstock, Corey Richardson, and Peter Atashian for reviewing drafts of this post.

  1. Rust, Swift, sort of Typescript, and all the functional languages who had it before it was cool. 

  2. Lookin’ at you, Go. 

  3. Moooooooooooooooonads 

  4. Types are not exactly sets due to some differences, but for the purposes of this post we can think of them like sets. 

  5. Though you can argue that strings often have their length bounded by the pointer size of the platform, so it’s still a finite set. 

  6. This even holds for zero-sized types, for more examples, check out this blog post 

  7. Like with strings, in practice this would probably be bounded by the integer type chosen 

Mitigating Underhandedness: Fuzzing Your Code

Posted by Manish Goregaokar on March 02, 2017 in programming, rust

This may be part of a collaborative blog post series about underhanded Rust code. Or it may not. I invite you to write your own posts about underhanded code to make it so!

The submission deadline for the Underhanded Rust competition has been extended, so let’s talk more about how to keep your code working and free from bugs/underhandedness!

Previously, we talked about Clippy.

Now, really, underhanded bugs are just another form of bug. And how do we find bugs? We test!

We write unit tests. We run the code under Valgrind, ASan, MSan, UBSan, TSan, and any other sanitizer we can get our hands on. Tests tests tests. More tests. Tests.

But, there’s a problem here. You need to write test cases to make this work. These are inputs fed to your code after which you check whatever invariants your code has. There’s no guarantee that the test cases you write will exercise all the code paths in your program. This applies for sanitizers too, sanitizers are limited to testing the code paths that your test cases hit.

Of course, you can use code coverage tools to ensure that all these code paths will be hit. However, there’s a conflict here – your code will have many code paths that are not supposed to be hit ever. Things like redundant bounds checks, null checks, etc. In Rust programs such code paths generally use panics.

Now, these code paths are never supposed to be hit, so they’ll never show up in your code coverage. But you don’t have a guarantee that they can never be hit, short of formally verifying your program. The only solution here is writing more test cases.

Aside from that, even ignoring those code paths, you still need to manually write test cases for everything. For each possible code path in your code, if you want to be sure.

Who wants to manually write a million test cases?

Enter fuzzing. What fuzzing will do is feed your program random inputs, carefully watching the codepaths being taken, and try to massage the inputs so that new, interesting (usually crashy) codepaths are taken. You write tests for the fuzzer such that they can accept arbitrary input, and the fuzzer will find cases where they crash or panic.

One of the most popular fuzzers out there is AFL, which takes a binary and feeds it random input. Rust has a library that you can use for running AFL, however it currently needs to be run via a Docker image or needs a recompilation of rustc, since it adds a custom LLVM pass. We’re working on making this step unnecessary.

However, as of a few weeks ago, we now have bindings for libFuzzer, which uses existing instrumentation options built in to LLVM itself! libFuzzer works a bit differently; instead of giving it a binary, you write a function in a special way and give it a library containing that function, which it turns into a fuzzer binary. This is faster, since the fuzzer lives inside the binary itself and it doesn’t need to execute a new program each time.

Using libFuzzer in Rust is easy. Install cargo-fuzz:

$ cargo install cargo-fuzz

Now, within your crate, initialize the fuzz setup:

$ cargo fuzz init

This will create a fuzzing crate in fuzz/, with a single “fuzz target”, fuzzer_script_1. You can add more such targets with cargo fuzz add name_of_target. Fuzz targets are small libraries with a single function in them; the function that will be called over and over again by the fuzzer. It is up to you to fill in the body of this function, such that the program will crash or panic if and only if something goes wrong.

For example, for the unicode-segmentation crate, one of the fuzz targets I wrote just takes the string, splits it by grapheme and word boundaries, recombines it, and then asserts that the new string is the same.

pub extern fn go(data: &[u8]) {
    // we only deal with unicode input
    // bail early, *without panicking* if the input isn't utf8
    if let Ok(s) = str::from_utf8(data) {
        // split into graphemes, recollect
        let result = UnicodeSegmentation::graphemes(s, true).flat_map(|s| s.chars()).collect::<String>();
        // recollected string should be the same as the input, panic if not
        assert_eq!(s, result);

        // split into words, recollect
        let result = s.split_word_bounds().flat_map(|s| s.chars()).collect::<String>();
        // recollected string should be the same as the input, panic if not
        assert_eq!(s, result);

The other targets ensure that the forward and reverse word/grapheme iterators produce the same results. They all take the byte slice input, attempt to convert to UTF8 (silently failing – NOT panicking – if not possible), and then use the string as an input testcase.

Now, these targets will panic if the test fails, and the fuzzer will try and force that panic to happen. But also, these targets put together exercise most of the API surface of the crate, so the fuzzer may also find panics (or even segmentation faults!) in the crate itself. For example, the fuzz target for rust-url doesn’t itself assert; all it does is try to parse the given string. The fuzzer will try to get the URL parser to panic.

To run a fuzz script:

$ cargo fuzz run fuzzer_script_1

This will start the fuzzer, running until it finds a crash or panic. It may also find other things like inputs which make the code abnormally slow.

Fuzzing can find some interesting bugs. For example, the unicode-segmentation fuzzers found this bug, where an emoji followed by two skin tone modifiers isn’t handled correctly. We’d probably never have been able to come up with this testcase on our own. But the fuzzer could find it!

The Rust Cap’n Proto crate ran cargo-fuzz and found a whole ton of bugs. There are more such examples in the trophy case (be sure to add any of your own findings to the trophy case, too!)

cargo-fuzz is relatively new, so the API and behavior may still be tweaked a bit before 1.0. But you can start taking it for a spin now, and finding bugs!