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);
}
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.
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.
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).
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.