Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0410660

Browse files
committedFeb 21, 2025
Add 'spaceship_operator' RFC;
1 parent 8830079 commit 0410660

File tree

1 file changed

+134
-0
lines changed

1 file changed

+134
-0
lines changed
 

‎text/0000-spaceship-operator.md

+134
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
- Feature Name: `spaceship_operator`
2+
- Start Date: 2025-02-21
3+
- RFC PR: TBA
4+
- Rust Issue: TBA
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Add the spaceship operator `<=>` to Rust, equivalent to calling `PartialOrd::partial_cmp` with the same operands.
10+
11+
# Motivation
12+
[motivation]: #motivation
13+
14+
Suppose a case where a three-way comparison was explicitly needed to be tested in all possible outcomes (for example, in a binary searcher).
15+
16+
A usual approach would be to use `if`-statements for all of these paths:
17+
18+
```rust
19+
let lhs = todo!();
20+
let rhs = todo!();
21+
22+
if lhs < rhs {
23+
todo!();
24+
} else if lhs == rhs {
25+
todo!();
26+
} else if lhs > rhs {
27+
todo!();
28+
} else {
29+
todo!();
30+
}
31+
```
32+
33+
This could also be simplified to:
34+
35+
```rust
36+
use core::cmp::Ordering;
37+
38+
let lhs = todo!();
39+
let rhs = todo!();
40+
41+
match lhs.partial_cmp(&rhs) {
42+
Some(Ordering::Less) => todo!(),
43+
Some(Ordering::Equal) => todo!(),
44+
Some(Ordering::Greater) => todo!(),
45+
None => todo!(),
46+
}
47+
```
48+
49+
This RFC simply proposes that the following would be possible as well:
50+
51+
```rust
52+
use core::cmp::Ordering;
53+
54+
let lhs = todo!();
55+
let rhs = todo!();
56+
57+
match lhs <=> rhs {
58+
Some(Ordering::Less) => todo!(),
59+
Some(Ordering::Equal) => todo!(),
60+
Some(Ordering::Greater) => todo!(),
61+
None => todo!(),
62+
}
63+
```
64+
65+
# Guide- and reference-level explanation
66+
[guide-and-reference-level-explanation]: #guide-and-reference-level-explanation
67+
68+
Usage of this operator will map identically to calling `PartialOrd::partial_cmp` (or `Ord::cmp` if this is desired instead):
69+
70+
```rust
71+
use core::cmp::Ordering;
72+
73+
// `T` and `U` are unspecified here, but both im-
74+
// plement `PartialOrd` for demonstration's sake.
75+
76+
let lhs: T = todo!();
77+
let rhs: U = todo!();
78+
79+
// `cmp0` and `cmp1` are defined completely equiva-
80+
// lently:
81+
82+
let cmp0: Option<Ordering> = <T as PartialOrd<U>>::partial_cmp(&lhs, &rhs);
83+
let cmp1: Option<Ordering> = lhs <=> rhs;
84+
```
85+
86+
The operator would be implemented similarly to the existing ones (see [Appendix B](https://doc.rust-lang.org/nightly/book/appendix-02-operators.html) of the book).
87+
In cases where `PartialOrd` is not implement between the compared objects, a compilation error would be yielded.
88+
89+
# Drawbacks
90+
[drawbacks]: #drawbacks
91+
92+
The greatest drawback, as far as I can tell, would be that this operator may seem confusing to programmers that are unfamiliar with this construct in other languages (see [prior art](prior-art) for a list hereof).
93+
But the same can also be said for a lot of other features in Rust.
94+
95+
# Rationale and alternatives
96+
[rationale-and-alternatives]: #rationale-and-alternatives
97+
98+
The main (and perhaps only) use for this operator would be to reduce boilerplate in cases where three-way comparisons are needed.
99+
100+
There is the possibility that this operation is not considered fundamental enough for an operator to be considered worthwhile.
101+
Currently, Rust has the `PartialOrd::partial_cmp` and `Ord::cmp` methods that achieve the same *operation* as proposed here.
102+
Not merging this RFC would (obviously) not change status quo, and thus one would simply resort to using either of these functions instead.
103+
104+
I personally believe that the same reasonings for the preexisting operators also apply (to some extent) in this case.
105+
I will, however, concede that the demand for this operator will not be as great as that for e.g. `==`, but in the end this will also be a matter of taste.
106+
107+
# Prior art
108+
[prior-art]: #prior-art
109+
110+
The *spaceship* operator itself currently exists in other languages, albeit with slightly different syntaxes.
111+
112+
## C++
113+
114+
In C++, this feature has existed since ISO/IEC 14882:2020 (C++20), stable since 15 December 2020 (see [`cppreference.com`](https://en.cppreference.com/w/cpp/language/operator_comparison)).
115+
C++, however, does not strictly define the precise semantics of this operator, merely requiring that the returned type must itself be comparable against `0`.
116+
117+
For integer types, such as `int` or `long long int`, the returned type is `::std::strong_ordering`, which is equivalent to Rust's `core::cmp::Ordering`.
118+
Floating-point types, like `long double` or `::std::bfloat_16`, on the other hand, return `::std::weak_ordering`, which is equivalent to Rust's `Option<core::cmp::Ordering>`.
119+
120+
## PHP
121+
122+
PHP has had the spaceship operator since PHP 7, stable since 1 December 2015 (see the [PHP Manual](https://www.php.net/manual/en/language.operators.comparison.php)).
123+
Here, it is defined as returning `-1` as an equivalent to our `Some(Ordering::Less)`, `0` as an equivalent to our `Some(Ordering::Equal)`, and `1` as an equivalent to our `Some(Ordering::Greater)`.
124+
125+
The operator is only defined for integral types.
126+
127+
## Perl
128+
129+
Perl defines the spaceship operator the same way as PHP (see the [Perldoc Browser](https://perldoc.perl.org/perlop#Equality-Operators)), with the added support for floating-point types (where `NaN` yields `undef` as an equivalent to our `None`).
130+
131+
# Unresolved questions
132+
[unresolved-questions]: #unresolved-questions
133+
134+
- Should this operator map to `PartialOrd::partial_cmp` or `Ord::cmp`? <sub>(Maybe obvious, but still worth discussing).</sub>

0 commit comments

Comments
 (0)
Please sign in to comment.