This is an informal proposal (not really Pre-RFC stage, just looking for opinions) of traits for overloading &&
and ||
using the power of the ControlFlow
type to preserve short circuiting behaviour.
pub trait BoolAnd<R = Self>{
type Output;
fn eval(self) -> ControlFlow<Self::Output,Self>;
fn booland(self, rhs: R) -> Self::Output;
}
(BoolOr
would look exactly the same, but boolor
instead of booland
).
With the above trait, the proposed desugaring for non-primitive x && y
is:
match BoolAnd::<typeof(y)>::eval(x){
Continue(__x) => BoolAnd::booland(__x,y),
Break(__out) => __out
}
There would be a logical invariant that BoolAnd::booland
/BoolOr::boolor
agrees with {BoolAnd,BoolOr}::eval
- IE. if both values individually return Continue
, doing a chained BoolAnd::eval
on the Output
should yield Continue
, and if either x
or y
breaks with a value after BoolAnd::eval
, BoolAnd::eval(BoolAnd::booland(x,y))
should return Break
with a logically equivalent value.
Further, BoolAnd::eval
should break for "falsey" values and continue for "truthy" values, and BoolOr::eval
should break for "truthy" values and continue for "falsey" values.
An alternative design used Self
for both the RHS and the Output type. I elected to go with this design to show the flexibility of the design but it could be altered to single-type only easily enough. Further, with a combination of assocated_type_defaults, and unstable features, the trait could be extended on nightly only from a single-type design to this one.