Why Enum with value is a function type that produces itself

Today I just realize that if an enum takes values, it is a static function type that returns itself.

for example

enum Option<T> {
    Some(T),
    None
}

Basically here we can use Option::Some as type 'static Fn(T) -> Option::Some.

This is really cool, but it's even coller if someone could tell me where this is addressed in the Rustbook.

1 Like

Tuple types create a constructor function with their same name. The constructor return type is the tuple type.

Tuple enum variants also create constructors. Their return type is the containing enum type (e.g., Option). (This is because enum variants aren't currently types in their own right.)

Playground demonstration that these are fns.

The constructors may predate RFCs? I think I've only seen passing reference to them in the documentation, e.g. under Tuple Struct Expression, so I'm afraid I don't know where this is further officially addressed.

Edit: Oh, and in case it helps clarify -- there is a separate namespace for values (like functions) and types, which is how the constructor and type can coexist.

3 Likes

This is handy too for mapping. In the case of Option:

vec.iter().map(|i| whatever(i)).map(Some).foobar();
3 Likes

This is really just a natural consequence of understanding what data constructors are in a compiler.

Imagine you had the following code:

fn f(x: f32) -> f32 { x*x }

fn main() {
    let f(x) = 9.0;
    println!("{}", x);
}

This program will fail to compile:

error[E0532]: expected tuple struct or tuple variant, found function `f`
 --> src/main.rs:5:9
  |
5 |     let f(x) = 9.0;
  |         ^ not a tuple struct or tuple variant

Interestingly, it doesn't fail to compile because it doesn't make sense. It does make sense, and the solution is x = 3.0. The problem is that the compiler cannot automatically compute the inverse of a function. (I believe it still an open question of whether there exist functions that have no inverses, but regardless, it is not computationally feasible to do this.) But if we could magically compute function inverses, there'd be no reason to not allow this kind of code.

In the meantime, the compiler has special builtin functions that it does understand how to invert: struct and tuple constructors. Because they are builtins with fixed, trivial semantics, they are allowed in patterns like this. (And because struct constructors have special syntax, you don't use function call syntax in patterns either, but this Fblah { x } syntax instead. Though there's no fundamental reason structs can't use function call syntax, as they already do for user-defined constructors.)

In other words, pattern matching is just algebra, and data constructors are just functions whose inverses are known to the compiler.

1 Like
fn f(x: f32) -> f32 { 0 }

fn main() {
    let f(x) = 9.0;
    println!("{}", x);
}
3 Likes

As @alice demonstrated, any function which maps multiple inputs to the same result has no inverse.

2 Likes

That's right, I was actually thinking about one-way functions. It's not really important to the overall idea though, which is that we can't compute the inverses of arbitrary functions.

1 Like