This often comes up in discussions about type systems, particularly if folks are moving from Typescript (structural types) to Rust (nominal types).

Nominal Types

The word “nominal” is derived from latin and “related to names”. In the context of type systems, this means that types are only equivalent if they are literally the same type. Usually this means that they have the same name, but in Rust, say, this actually means they represent the same unique symbol. This means that if a function takes the type Doodle in its argument list, you gotta pass it a Doodle, nothing else will do.

Structural Types

“Structural” means “related to structure”. In type systems, this means that types are considered equivalent if they have at least the same structure, it doesn’t matter if they are represented by differently named types in the source (or different symbols under the hood) as long as they have the same fields. This means that if a function usually takes a type Doodle in its argument list, you can pass it anything that has the same structure as a Doodle.

Examples

#![allow(unused)] // Remove silly warnings
struct Doodle {
    values: Vec<u32>
}

struct Sketch {
    values: Vec<u32>
}

fn doodler(doodle: Doodle) -> () {
    ()
}

fn sketcher(sketch: Sketch) -> () {
    ()
}

fn main() {
    let doodle: Doodle = Doodle {
        values: vec![1, 2, 3]
    };
    let sketch: Sketch = Sketch {
        values: vec![3, 2, 1]
    };
    doodle(sketch); // Does not compile!
    sketch(doodle); // Does not compile!
}
   Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/main.rs:25:13
   |
25 |     doodler(sketch); // Does not compile!
   |     ------- ^^^^^^ expected `Doodle`, found `Sketch`
   |     |
   |     arguments to this function are incorrect

Even though the types are structurally equivalent, they represented by different types so this will not compile.

The situation is different when we switch over to Typescript, however:

type Doodle = {
    lines: Array<number>
}

type Sketch = {
    lines: Array<number>
}

const sketcher = (sketch: Sketch) => {}
const doodler = (doodle: Doodle) => {}
const main = () => {
    let doodle: Doodle = {
        lines: [1, 2, 3]
    }
    let sketch: Sketch = {
        lines: [3, 2, 1]
    }
    sketcher(doodle)
    doodler(sketch)
}

The above typescript will happily compile and run.