Python-like chained comparison operators

The examples in this thread that I find easily parseable, and thus not confusing, never involve more than two comparison operators. if 0 < x <= 10 is such a case. Note that this still leaves an implementation ambiguity: does the compound comparison use short-circuit evaluation (&&) or not (&). That issue does not arise in the conventional mathematical expression because relationships are not imperative, but it does arise in code execution, which (for conventional processors) always is imperative. If Rust, in keeping with its principles of avoiding non-intuitive behavior, were to provide both forms of evaluation, then some additional marker on the compound comparison is needed to indicate whether the combining operator is & or &&.

There are at least three obvious ways to specify whether the evaluation should short-circuit (first case) or not (the remaining two cases)

(0 < x) && (x <= 10)
(0 < x) & (x <= 10)
x in 1 ..= 10

None of these is very painful to write, and each makes clear whether or not the right-hand <= comparison is mandatory. However the first two forms do require that the expression x be evaluated twice, which is redundant and would have unintended consequences if x has side effects. One possible alternative would be to use the _ sigil to represent the immediately-prior expression, thus incurring only a single right-hand-side expression evaluation of x:

(0 < x) && (_ <= 10)
(0 < x) & (_ <= 10)
1 Like

I wouldn't worry too much about confusion due to short-circuiting. The difference is only observable for types whose comparison operators have side-effects, which should be very rare, and which are probably already confusing in other ways.

In the vast majority of cases, the semantics are the same regardless of short-circuiting, so the optimizer can even turn the non-short-circuiting version into a short-circuiting one.

Note that (1..=10).contains(x) actually is implemented using short circuiting, currently.

3 Likes

But in that form x is only evaluated once, so there can't be observable side effects from evaluating x an unpredictable number of times.

1 Like

x.eq is evaluated multiple times and could have side effects. (actually looking at the source it's called on the items, but that is not necessary).

That's not the only way short-circuiting would be an issue.

If you write a() < b() < c(), and the compiler turns that into (say) { let a = a(); let b = b(); a < b && b < c() } then c() won't get evaluated if a() < b() is false. If the compiler instead desugars that into { let a = a(); let b = b(); let c = c(); a < b && b < c } then c() will always get evaluated.

That's in addition to any unusual cases that might arise with a comparison operator that has observable side effects.

8 Likes