I devised a simple and efficient scheme to do overloading based on traits.
Suppose that t1
implements Trait1
and Trait2
. You can select which trait to use for each argument to a function in the following way:
f(AsTrait1(&t1), AsTrait1(&t1));
f(AsTrait1(&t1), AsTrait2(&t1));
This is useful when the implementation of f
can be made more performant, the result more accurate, and so on, depending on the available traits.
The implementation of f
looks like this:
pub fn f<T1: AsTrait1Or2, T2: AsTrait1Or2>(x: T1, y: T2) -> FResult {
// NOTE: It's important to ALWAYS check for exhaustiveness.
match (T1::TRAIT, T2::TRAIT) {
(AllTraits::Trait2, _) => FResult::Str(x.t2().method2()),
(_, AllTraits::Trait2) => FResult::Str(y.t2().method2()),
(AllTraits::Trait1, AllTraits::Trait1) => FResult::IntInt(
x.t1().method1(), y.t1().method1()),
}
}
There's only a single implementation and you can inspect the available traits and behave accordingly without having to worry about performance because T1::TRAIT
and T2::TRAIT
are associated consts, so the match expression and any related conditional logic are optimized away after monomorphization.
This is cool because you can write the logic in regular Rust, which is much more powerful than any automatic but fixed overloading resolution.
See the proof of concept and detailed presentation to learn more.