# \`ControlFlow\`: The Answer to \`BoolAnd\`/\`BoolOr\`

**URL:** <https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608>\
**Category:** language design\
**Created:** [September 22, 2023, 1:59pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608 "2023-09-22T13:59:25Z")\
**Posts on this page:** 16\
**Page:** 1

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [September 22, 2023, 1:59pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/1 "2023-09-22T13:59:25Z")

</div>

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.

```rust
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:

```rust
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.

---

<div class="post-metadata">

**Author:** ![Mokuz](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mokuz/32/7225_2.png) [@Mokuz](https://internals.rust-lang.org/u/Mokuz)\
**Post date:** [September 22, 2023, 3:22pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/2 "2023-09-22T15:22:21Z")

</div>

What is the motivation for this proposal?

And does the trait necessarily need two methods?

```rust
// which types would you implement this trait for?
trait Booland<Rhs = Self> {
    type Output; // what is the point of output? Shouldn't this always return bool?
    // taking in closure would signal that `rhs` might not get evaluated
    fn booland(self, rhs: impl FnOnce() -> Rhs) -> Self::Output;
}

```

Last I feel the names `LogicalAnd` & `LogicalOr` are more descriptive.

---

<div class="post-metadata">

**Author:** ![SkiFire13](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skifire13/32/7579_2.png) [@SkiFire13](https://internals.rust-lang.org/u/SkiFire13)\
**Post date:** [September 22, 2023, 3:36pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/3 "2023-09-22T15:36:31Z")

</div>

I would expect the `y` in `x && y` to be able to influence control flow (think for example of `.await`!) , and `FnOnce` can't express that.

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [September 22, 2023, 3:59pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/4 "2023-09-22T15:59:29Z")

</div>

I'm not sure mine expresses that ether. Though mine would IMO be nicer to lower, and avoids the APITIT.

---

<div class="post-metadata">

**Author:** ![SkiFire13](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skifire13/32/7579_2.png) [@SkiFire13](https://internals.rust-lang.org/u/SkiFire13)\
**Post date:** [September 22, 2023, 4:50pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/5 "2023-09-22T16:50:25Z")

</div>

I think yours would be able to express that because it still evaluates `y` in the same function/loop scope.

That said, the problem of your desugaring is that `typeof(y)` is not valid rust, and even though it can probably implemented for special cases in the compiler it would no longer be a desugaring in that case.

---

<div class="post-metadata">

**Author:** ![InfernoDeity](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/infernodeity/32/7588_2.png) [@InfernoDeity](https://internals.rust-lang.org/u/InfernoDeity)\
**Post date:** [September 22, 2023, 5:39pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/6 "2023-09-22T17:39:08Z")

</div>

> [@SkiFire13](#):
>
> That said, the problem of your desugaring is that `typeof(y)` is not valid rust, and even though it can probably implemented for special cases in the compiler it would no longer be a desugaring in that case.

`typeof` is applied for desugarings of other operators, after type inference, is it not? It's really just a placeholder to show which impl of `BoolAnd` is used.

> [@SkiFire13](#):
>
> I think yours would be able to express that because it still evaluates `y` in the same function/loop scope.

Ah you meant control flow to external functions.

---

<div class="post-metadata">

**Author:** ![quinedot](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/quinedot/32/7294_2.png) [@quinedot](https://internals.rust-lang.org/u/quinedot)\
**Post date:** [September 22, 2023, 10:34pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/7 "2023-09-22T22:34:56Z")

</div>

I believe it's just inference for other operators (though it's true they're not really "desugarings" at the syntax level).

[The inference situation is different](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f8f12cc72da0bbdedb86ea61b97b4fe2) because other binary operators take both arguments in a single call. Note that any implemented `R` would compile there -- some sort of `typeof` is _required_ for said desugaring. Which in turn means the RHS must have an inferrable type on its own, so this wouldn't work with multiple implementations unless one of them was for `!`,\[1\] say.

```rust
let thing = thing && panic!();

```

The single-type version doesn't have these issues.

* * *

1. or maybe `()` today due to the fallback blocking stabilization of `!`

---

<div class="post-metadata">

**Author:** ![SkiFire13](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/skifire13/32/7579_2.png) [@SkiFire13](https://internals.rust-lang.org/u/SkiFire13)\
**Post date:** [September 23, 2023, 7:01am UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/8 "2023-09-23T07:01:42Z")

</div>

> [@InfernoDeity](#):
>
> `typeof` is applied for desugarings of other operators, after type inference, is it not? It's really just a placeholder to show which impl of `BoolAnd` is used.

AFAIK other operators are just lovered to `OpTrait::op_method(arg1, arg2)` and the types here are fully identified by the parameter passed. In your case however in `BoolAnd::eval(x)` there's nothing that tells the compiler what the `R` parameter should be.

See for example [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=9a6d4cd202143cd67a0b0ab2f4d425b5)

---

<div class="post-metadata">

**Author:** ![HjVT](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/hjvt/32/10365_2.png) [@HjVT](https://internals.rust-lang.org/u/HjVT)\
**Post date:** [September 25, 2023, 5:26pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/9 "2023-09-25T17:26:16Z")

</div>

I don't think generic parameter R is particularly useful?

---

<div class="post-metadata">

**Author:** ![scottmcm](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/scottmcm/32/2355_2.png) [@scottmcm](https://internals.rust-lang.org/u/scottmcm)\
**Post date:** [September 25, 2023, 8:26pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/10 "2023-09-25T20:26:17Z")

</div>

Some prior conversations about using `ControlFlow` for this: [https://github.com/rust-lang/rfcs/pull/2722#issuecomment-832198425](https://github.com/rust-lang/rfcs/pull/2722#issuecomment-832198425)

---

<div class="post-metadata">

**Author:** ![pnkfelix](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/pnkfelix/32/110_2.png) [@pnkfelix](https://internals.rust-lang.org/u/pnkfelix)\
**Post date:** [September 27, 2023, 7:38pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/11 "2023-09-27T19:38:45Z")

</div>

> [@Mokuz](#):
>
> Last I feel the names `LogicalAnd` & `LogicalOr` are more descriptive.

Better still might be `ShortCircuitAnd` and `ShortCircuitOr`; without knowing about the intended use cases for overloading this, I'm not sure whether attaching "Bool" or "Logical" to it makes sense.

---

<div class="post-metadata">

**Author:** ![pitaj](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/pitaj/32/11262_2.png) [@pitaj](https://internals.rust-lang.org/u/pitaj)\
**Post date:** [September 27, 2023, 9:00pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/12 "2023-09-27T21:00:03Z")

</div>

`Scand` and `Scor` perhaps?

---

<div class="post-metadata">

**Author:** ![pnkfelix](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/pnkfelix/32/110_2.png) [@pnkfelix](https://internals.rust-lang.org/u/pnkfelix)\
**Post date:** [September 27, 2023, 9:10pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/13 "2023-09-27T21:10:15Z")

</div>

Why shorten them, especially by so much? I don't expect them to show up often (if at all?) in trait bounds, and when/if they do show up, people can do that rename locally.

---

<div class="post-metadata">

**Author:** ![djc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/djc/32/1592_2.png) [@djc](https://internals.rust-lang.org/u/djc)\
**Post date:** [September 29, 2023, 8:50am UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/14 "2023-09-29T08:50:04Z")

</div>

It would be great to have a solution for this -- I've definitely thought about DSLs that could benefit from being able to override short-circuiting `And` and `Or` operations.

---

<div class="post-metadata">

**Author:** ![bugaevc](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/bugaevc/32/13651_2.png) [@bugaevc](https://internals.rust-lang.org/u/bugaevc)\
**Post date:** [September 30, 2023, 3:37pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/15 "2023-09-30T15:37:14Z")

</div>

Previous discussion: [Pre-RFC: Overload Short Curcuits](https://internals.rust-lang.org/t/pre-rfc-overload-short-curcuits/10460)

---

<div class="post-metadata">

**Author:** ![system](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/system/32/14092_2.png) [@system](https://internals.rust-lang.org/u/system)\
**Post date:** [December 29, 2023, 3:37pm UTC](https://internals.rust-lang.org/t/controlflow-the-answer-to-booland-boolor/19608/16 "2023-12-29T15:37:50Z")

</div>

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.
