I don’t like the proposed approach that much, especially matching looks quite bad:
match x {
(_ | _)::0(val) => assert_eq!(val, 1_i32),
_::1(_) => unreachable!("Value was set to the first variant")
};
match bar {
(i64 | () | i64 | (i64, f64))::3((a, b)) => a == -3_i64 && b == 0.0_f64,
(_ | _ | _ | _)::2(_) => false,
_::1(_) => false,
_ => false
}
It looks like pile of emojis to me and Rust is already perceived as a language a bit too heavy on sigils.
Bad humour inside
I wonder if following the “turbo-fish”, match combination (_|_):: will be named as a “turbo-butt”.
I think a better approach will be to introduce refined enums, which can look like this:
enum MyError {
A,
B(f32),
C(String),
D,
}
// `MyError[A, B]` (the exact syntax is up to bikeshedding) is essentially
// an alias for `MyError`, but compiler knows that only variants A and B
// can be used with it
fn foo() -> Result<(), MyError[A, B]> { .. }
// we can use aliases to make code more readable
type BarError = MyError[A, D];
fn bar() -> Result<(), BarError> { .. }
fn baz() -> Result<(), MyError> {
match foo() {
Ok(()) => (),
Err(MyError::A) => { .. },
Err(MyError::B(val)) => { .. },
// we can omit variants C and D, compiler will not complain and
// on desugaring will add `_ => unsafe { std::intrinsics::unreachable() }`
}
match bar() {
Ok(()) => (),
// refined alias can be implicitly coerced to the parent type
// (after all it's just an alias)
Err(err) => return Err(err),
}
}
// compile error with message indicating that variant D can not be used with
// the given enum refinement
fn foo1() -> MyError[A] { MyError::D }
// compiler error: foo can return Err(MyError::B), while only variants A and D
// were expected
fn foo2() -> Result<(), BarError> {
foo()
}
// enum refinement can be coerced not only to the parent type, but also to other
// refinement if it's wider than the coerced one
fn foo3() -> Result<(), MyError[A, B, C]> {
foo()
}
// we could warn on unnecessary matches
fn foo3() {
match foo() {
Err(MyError::D) => (), // warning: unreachable match arm
_ => (),
}
}
This approach also automatically solves automatic trait implementation concerns, as programmer will be in full control over which traits will be implemented for parent enum.