Overloading Keyword Generics
In the previous section we saw that we cannot use join
to await two futures
concurrently because in "base Rust" we cannot run two closures concurrently. The
capabilities introduced by the superset (async) have no counterpart in the
subset ("base Rust"), and therefore we cannot write it.
But sometimes we do want to be able to specialize implementations for a specific context, making use of the capabilities they provide. In order to do this we need to be able to declare two different code paths, and we propose effect overloading as the mechanism to do that.
This problem is not limited to async Rust either; const implementations may want to swap to platform-specific intrinsics at runtime, but keep using portable instructions during CTFE. This is only a difference in implementation, and should not require users to switch between APIs.
The way we envision effect overloading to work would be similar to
specialization. A base implementation would be declared, with an overload in the
same scope using the same signature except for the effects. The compiler would
pick up on that, and make it work as if the type was written in a polymorphic
fashion. Taking our earlier example, we could imagine the sum
function could
then be written like this:
#![allow(unused)] fn main() { // Sum the output of two functions: default fn sum<T>( lhs: impl FnMut() -> T, rhs: impl FnMut() -> T ) -> T { lhs() + rhs() } async fn sum<T>( lhs: impl async FnMut() -> T, rhs: impl async FnMut() -> T ) -> T { let (lhs, rhs) = (lhs(), rhs()).join().await; lhs + rhs } }
We expect effect overloading to not only be useful for performance: we suspect
it may also be required when defining the core (async) IO types in the stdlib
(e.g. TcpStream
, File
). These types carry extra fields which their base
counterparts do not. And operations such as reading and writing to them cannot
be written in a polymorphic fashion.
While we expect a majority of ecosystem and stdlib code to be written using effect polymorphism, there is a point at which implementations do need to be specialized, and for that we need effect overloading.