Actually I think it would be nice if a type (A|A) could be equivalent to A. This would be perfectly sound the upper example, but I see that there are problems with generic code and âtype-based matchingâ.
Consider for example this:
fn maybe_panic<A, B>(val: (A | B)) {
match val {
a: A => (),
b: B => panic!(),
}
}
let b: u32 = 0;
maybe_panic::<i32, u32>(b); // -> panics
maybe_panic::<u32, u32>(b); // -> not sound
While with the structure based approach there is perfect determinism here:
fn maybe_panic<A, B>(val: (A | B)) {
match val {
_::0(_) => (),
_::1(_) => panic!(),
}
}
let b: u32 = 0;
maybe_panic::<i32, u32>(_::1(b)); // -> panics
maybe_panic::<u32, u32>(_::1(b)); // -> panics
But I find the possibility of having anonymous variants with equal types very unintuitive and I think that it might lead to very unreadable and therefore errorprone code.
One Idea out of this would simply to disallow constructing such variant types.
Another idea would be to handle it via bounds on the types of generic parameters. We could add bounds for specifying that A != B and in cases where this bound is not given a special case where A == B must be constructed in the match statement:
fn maybe_panic<A, B>(val: (A | B)) {
match val {
a: A => (),
b: B => panic!(),
ab: A + B => (),
}
}
fn maybe_panic<A, B>(val: (A | B))
where A != B
{
match val {
a: A => (),
b: B => panic!(),
ab: A + B => (),
}
}
(I know Iâm mixing types with trait bounds syntax here but this is just the first possible syntax I came up with.)