EDIT: the following posts are the most notable one (click on them, reading them is worth your time)
END OF THE SUM-UP
When reading the thread about go generics, I had an idea that I think is worth sharing.
Let's assume that
- the syntax
(i32 | &str)
would create an anonymous enum with two variants (i32
and&str
). - anonymous enum automatically implements all traits implemented by all the variants.
if
andmatch
expression would be extended to be able to return value of different types for each branch. The type of this expression would be either an anonymous enum (or a regular enum iif the enum implements theFrom
trait for each possible branch).for
can be used as an expression, and can iterates on tuples.- Inside the loop, the type of the value would be an anonymous enum with all the types of the tuple.
- If all values generated by the loop share the same type, the value created by this statement would be a
[T]
or, if the size is known (nobreak
/continue
/…) to a[T; n]
. - If the types aren't the same, if could be collected as a
[(A | B | C | ...)]
withA
,B
,C
, … the types of the elements or if the size is known in a tuple(A, B, C)
. - Of course iterator would be extended to support the same functionality (
(1f, 2u, "three").iter()
would implementIterator<Item=(f32 | usize | &str)>
).
This open some interesting design ideas:
First, let's play with if
block.
// explicitly create an anonymous enum
let x: (i32| &str) = 3;
let cond = false;
let y: (i32| &str) = if cond {
3
} else {
"three"
}
assert_eq!(x, y);
let y = if cond {
3
} else {
"three"
} : (i32| &str); // using type ascription since `3` is ambiguous
assert_eq!(x, y);
let y = if cond {
3i
} else {
"three"
};
assert_eq!(x, y);
Now, with loops:
let tuple = (1.0f, 2u, "three");
let (x, y, z) = for value in tuple {
// value's type is `(f32 | usize | &str)` so value implements `Debug`
printf("{}", value); // value of heterogeneous types can be used as long as they share some type in common, but only through those traits
value
}; // implicit `.collect()` at the end of a for expression
// x is a f32, y an usize, and z a str
assert_eq!(tuple, (x, y, z));
and finally, let's play with functions:
trait SuperTrait {}
trait SomeTrait: SuperTrait {}
fn test<T: SomeTrait>(value: T) -> bool;
struct A;
struct B;
struct C;
impl SomeTrait for A {}
impl SomeTrait for B {}
impl SomeTrait for C {}
fn foo(values: (A, B, C) -> impl SuperTrait {
for value in values {
// `values` is a tuple of type `(A, B, C)` so the type of `value` is
// the anonymous enum `(A | B | C)` which implements `SomeTrait`.
if test(value) {
return value; // the returned type can be any of `A`, `B` or `C`
}
}
}
// the concrete type of `foo` is `(A | B | C)` which implements `SomeTrait`
// and `SuperTrait` (since `SuperTrait` is required by `SomeTrait`).
I think it's really nice how everything could fit together. All the types would be statically typed, but it really feels that I was using some dynamically typed language.