Anonymous enums?

Two basic ways to make an array of “mixed” items to be printed:

use std::fmt::Display;

enum Thing {
    Int(i32),
    Text(&'static str)
}

fn show_things1(things: &[Thing]) {
    for t in things.iter() {
        match t {
            &Thing::Int(n) => println!("{}", n),
            &Thing::Text(s) => println!("{}", s),
        }
    }
}

fn show_things2(things: &[&Display]) {
    for t in things.iter() {
        println!("{}", t);
    }
}

fn main() {
    let arr1 = [Thing::Int(10), Thing::Text("hello")];
    show_things1(&arr1);

    let arr2: [&Display; 2] = [&10, &"hello"];
    show_things2(&arr2);
}

But just like tuples are sometimes handy when you don’t want/need to define a struct and name all its fields, could anonymous enums be sometimes useful in Rust code?

fn show_things1b(things: &[u32 | &'static str]) {
    for t in things.iter() {
        match t {
            u32(n) => println!("{}", n),
            &'static str(s) => println!("{}", s),
        }
    }
}

fn main() {
    let arr1b: [u32 | &'static str; 2] = [10, "hello"];
    show_things1b(&arr1b);
}
2 Likes

It would surely be a cool feature. Perhaps the appropriate match syntax would be the current @ syntax for simple patterns like your example:

match t {
    n @ u32 => println!("{}", n),
    s @ &'static str => println!("{}", s),
}

As for usefulness… I can’t say I have really missed it, myself, except in perhaps a couple of cases. My enums are often too ambiguous for this, unless I’m just bundling a bunch of types. That said, maybe that’s what people in general do. :slight_smile:

Or take a page from the syntax for embedding types in expression position (since this is embedding types in pattern position):

match t {
    n @ <u32> => ...,
    s @ <&'static str> => ...,
}

Or extend patterns to allow for type ascription (which would be handy in a few places):

match t {
    n: u32 => ...,
    s: &'static str => ...,
}

Oh yes. I’d like to have it. Is there any RFC for this?

There was an anonymous enum RFC using a different syntax:

/cc @canndrew

Yeah, there was an RFC for this but it wasn’t very popular. People thought it added too much weight to the language for not enough benefit and, for now, I agree. By far the most important part of that RFC was turning ! into a type and it now looks like that might be going to happen.

However there is another RFC to enable generically-sized tuples. If that gets implemented then anonymous enums would suddenly become much more useful and it would be worth considering this RFC again.

1 Like

It’s OK. Anonymous enums aren’t a feature I need particularly :slight_smile:

This idea is cool !!

It also will help compile such code in Rust:

let res = if g == 2 {
    MyStruct { val: 2 }
} else {
    2
};

Under hood compiler will generate the following anonymous enum type:

let res = if g == 2 { // let res : MyStruct | i32, where anonymous enum type MyStruct | i32
    MyStruct { val: 2 }
} else {
    2
};

No, please don't (recommend to) do that. If two distinct types automatically and unconditionally get unified to an anonymous sum type, then suddenly you can use any type anywhere, which basically gets rid of all the guarantees of static typing.

No, it will not rid of all the guaranties of static typing !!
This general enum under the hood and it is the same if I write it by hand !!

This code:

let res = if g == 2 { // let res : MyStruct | i32, where anonymous enum type MyStruct | i32
    MyStruct { val: 2 }
} else {
    2
};

match res {
  res @ MyStruct => println!("{}", n),
  res @ i32 => println!("{}", s),
}

is just a syntax sugar for this:

enum AnonymousEnum {
  FirstUndenotedName(MyStruct),
  SecondUndenotedName(i32)
};

let res: AnonymousEnum = if g == 2 {
    FirstUndenotedName(MyStruct { val: 2 })
} else {
    SecondUndenotedName(2)
};

match res {
  res @ MyStruct => println!("{}", n),
  res @ i32 => println!("{}", s),
}

You don't understand the point. I know what it would be transformed to. But it doesn't matter, and here's why. If we could just do that anywhere, and the compiler allowed it doing the whole transformation without any indication in the code, then it and – more importantly – readers of the code can't possibly know whether I intended to make an anonymous sum type, or I'm just mixing up my types by accident.

It is also not an issue !!
We can introduce the new syntax:

let res := if g == 2 { // Ok, anonymous type inference is allowed
    MyStruct { val: 2 }
} else {
    2
};

match res {
  res @ MyStruct => println!("{}", n),
  res @ i32 => println!("{}", s),
}
let res = if g == 2 {  // Error, anonymous type inference is not allowed here, please use := syntax
    MyStruct { val: 2 }
} else {
    2
};

match res {
  res @ MyStruct => println!("{}", n),
  res @ i32 => println!("{}", s),
}

Sure, I know that by introducing new syntax the explicitness problem goes away, but only partially. However that's not what you were suggesting initially.

Anyway, this kind of magic is still dangerous, and the proposed syntax is neither necessary nor very good, as it is hard to distinguish from plain = and the use of the semicolon is not evocative of the creation of a new type. More importantly, the automatic, unguided inference of a type can still hide a mistake even with the explicit syntax, becase what if you accidentally add a third type that you didn't intend to have? It would now be accepted and automatically added to the sum. That is still really bad.

Furthermore, anonymous sum types are far more useful in function return position. And at that point, explicit type annotations are even more superior for declaring an anonymous type for another reason: they do not require new syntax in an unrelated part of the language (name bindings).

Completely agree with this statement and the reason that I consider this proposal, because I have my own proposal with Simplification error handling https://internals.rust-lang.org/t/simplify-error-handling/12226/20

Take a look at the end of discussion where is mentioned anonymous types :wink:

I have seen that proposal. I haven't replied to it because there have been tons of almost identical proposals regularly, to which I am getting somewhat tired replying the same things over and over again. In short, I don't think Rust needs exception handling with exception-like terminology because it's confusing, and merely transforming trivial syntax sugar into a core language syntax doesn't have any real benefits but it makes the language more complex. Furthermore, as others have pointed it out already (so again, I didn't feel the need to re-iterate their point), that proposal adds what is effectively global type inference, to which my concerns above also apply, if not doubly so, because of the additional problem of hiding important details in API boundaries.

But I do not think that it is an issue !!
If library writer wants use anonymous enum it is its choice !!

Also I have updated proposal with considering to introduce anonymous enum as the error result

This thread is very old and not really on topic for this forum.

3 Likes