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.