Daily Note - 2025-02-26
Hey, I'm Hanno! These are my daily notes on Crosscut, the programming language I'm creating. If you have any questions, comments, or feedback, please get in touch!
Let's build on yesterday's example about structural typing. If we don't want to repeat our type all the time, we can define it once and give it a name:
type Measurement = (Value, Unit);
fn measure_width() -> Measurement { ... }
fn measure_height() -> Measurement { ... }
The return type is no longer anonymous. But that
approach we used to give it a name, the type
declaration, doesn't change anything else.
It's still structural typing. Let me demonstrate:
type Width = (Value, Unit);
type Height = (Value, Unit);
fn measure_width() -> Width { ... }
fn measure_height() -> Height { ... }
Now the two function's return types have different
names. But they're still the same type. We can assign
both return values to the same variable, as we did
yesterday. Because they still have the same structure.
Tuples in Rust are structurally typed, and type
doesn't change that. It's just a type
alias.
Let's try something different:
struct Width(Value, Unit);
struct Height(Value, Unit);
fn measure_width() -> Width { ... }
fn measure_height() -> Height { ... }
Now we're no longer using tuples, nor type aliases. Those are tuple structs. And despite having the same structure, those two structs have different types. This no longer works:
let width = measure_width();
let height = measure_height();
let max_dimension = if width > height {
width
} else {
height
};
It won't compile, because the compiler can't figure out
what the type of
max_dimension
should be. It can't be both
Width
or Height
, because
that's a difference that matters now. They are different
types, with different names, defined in different
locations; despite having the same structure. This is
called nominal typing.