Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RFC: Add "spaceship" operator. #3776

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

bjoernager
Copy link

@bjoernager bjoernager commented Feb 21, 2025

Rust should have a spaceship operator <=> for cleaner three-way comparisons.

Rendered.

@peterjoel
Copy link

My feeling is that this doesn't add very much extra expressivity, especially considering the high cost of adding a new operator to the language.

@bjoernager
Copy link
Author

bjoernager commented Feb 21, 2025

My feeling is that this doesn't add very much extra expressivity, especially considering the high cost of adding a new operator to the language.

The goal of this isn't to increase expressiveness (per se); it's just to add a shorthand for an existing feature. :)

@ehuss ehuss added the T-lang Relevant to the language team, which will review and decide on the RFC. label Feb 22, 2025
@Noratrieb
Copy link
Member

This RFC lacks motivation other than it making code a bit shorter. We don't just add operators based on that, if we did we'd have hundreds of operators.
You should either add a lot more motivation on why exactly partial_cmp deserves an operator and many other more common functions don't, or close the RFC.

@clarfonthey
Copy link

clarfonthey commented Feb 22, 2025

In addition to the lack of motivation, the other big issue with this operator is I don't think that it's actually very obvious whether <=> should do PartialOrd or Ord.

To me, having to filter out None values on an operator like this feels extremely tedious since, for a large majority of the cases where I would use it (strings and integers), I would effectively be adding a None => unreachable!() branch. And even for floats, I would probably prefer using total_cmp instead of partial_cmp, which would not work with the operator since it's a separate function, not a trait impl.

And there's also the issue that the operator returns an enum that is not in the prelude, and so we'd have to manually import it, even though the operator itself is usable in the prelude. That feels at least kind of bad, and I'd assume that adding cmp::Ordering to the prelude is completely off the table due to the fact that even the standard library exports multiple Ordering types, and there are countless libraries that might have types named Less, Equal, and/or Greater that would conflict with importing the variants directly.

While I think it's cool, ultimately, there are too many unanswered design questions that the RFC doesn't really try to answer. While I think that the desire to get involved is always commendable, it really doesn't feel like you've properly thought this feature out enough for it to be viable as an RFC right now.

Note that this doesn't mean that I'd always be against a comparison operator, but that I feel that a substantial amount of design work would have to be done first that has not yet been done.

@bjoernager bjoernager marked this pull request as draft February 22, 2025 20:04
@bjoernager
Copy link
Author

You all have very compelling arguments. I will yield in that this proposal may not necessarily be mature enough for serious consideration as-is (other than the question of whether a <=> operator is something we even want – never mind its precise semantics). In any case, the most reasonable thing, I believe, would be to put it on hold until something more concrete has been formulated. It seems you also share this view, so I will mark it as a draft for now.

@scottmcm
Copy link
Member

scottmcm commented Feb 23, 2025

Note that MIR actually does have this as a BinOp, which will soon lower to a relatively new LLVM intrinsic.

One possibility: add both <=> and <==>, where one is cmp and the other is partial_cmp (dunno which is which), or other possibilities like <=?> (probably not <≟>, even if that's fun).

Or maybe it could use a "if it's Ord for all lifetimes, it's cmp, otherwise it's partial_cmp" magic definition?

Also, I don't think adding <=> would need to affect tokenization at all. We could say that the parser just looks at the existing <= and > (or <= and =>) tokens when matching the operator. Which would make it much easier to do than if it needed to add a new <=> token -- and in fact the parser is already parsing <=> today using that approach!

https://github.com/rust-lang/rust/blob/07697360aee0cebcb4e304236ba1884d8dde5469/compiler/rustc_parse/src/parser/expr.rs#L233-L245


That said, last I did a straw poll about whether people were interested in having it, the reception was lukewarm-to-negative. So I suspect it's unlikely to land, even if I'm vaguely in favour myself.

It would be nice in the derives particularly to have it avoid the extra dereference-and-inlining on primitives today that < and friends avoid by being primitive operators. But it's also true that that's not enough of a reason to add a whole publicly-visible operator.

@Lokathor
Copy link
Contributor

<=?> for partial_cmp seems... "not entirely unreasonable"

@kennytm
Copy link
Member

kennytm commented Feb 23, 2025

<=?> for partial_cmp seems... "not entirely unreasonable"

as a non-standard operator among other programming languages it would be hard to remember the spelling whether it should be <=?> or <?=> though 🙃

@Lokathor
Copy link
Contributor

The compiler would just tell you if you got it wrong, you'd learn in like 10 minutes probably.

also it "obviously" goes at the end because in expressions it's always a postfix operator, let x = some_fn()?;

@kennytm
Copy link
Member

kennytm commented Feb 23, 2025

also it "obviously" goes at the end because in expressions it's always a postfix operator, let x = some_fn()?;

that would suggest the operator should be named <=>?.

also the postfix ? "unwraps" an x: Option<T> to produce a x?: T, but for this operator it's sort-of the opposite that "wraps" from (a <=> b): Ordering to (a <=?> b): Option<Ordering>.

so it should be spelled <¿=>.

@Lokathor
Copy link
Contributor

i see what you're saying but also emotionally i want the ? to go inside the < > pair, you know what i mean?

@clarfonthey
Copy link

also it "obviously" goes at the end because in expressions it's always a postfix operator, let x = some_fn()?;

that would suggest the operator should be named <=>?.

also the postfix ? "unwraps" an x: Option<T> to produce a x?: T, but for this operator it's sort-of the opposite that "wraps" from (a <=> b): Ordering to (a <=?> b): Option<Ordering>.

so it should be spelled <¿=>.

This is actually why, although a similar thought to that mentioned came to mind, I actually didn't think it was a good idea after thinking about it.

It feels almost like x <=?> y would be a wrapper for (x <=> y)? which would make sense but still be very weird.

@scottmcm
Copy link
Member

a wrapper for (x <=> y)? which would make sense but still be very weird.

That's kinda fun, actually. x <=> y is Ord::cmp(&x, &y) and x <=>? y is PartialOrd::partial_cmp(&x, &y)? -- then both "operators" give you Ordering.

(I'm not convinced it's actually good, though.)

@bjoernager
Copy link
Author

Shouldn't it be <?=> for similarity with !=?

@nohenry
Copy link

nohenry commented Mar 1, 2025

What about <~> for partial_cmp and <=> for cmp? Having a ? makes it seem like we're propagating the option while ~ has a much more... "partial" feeling to me. Downside is ~ is annoying to type :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

9 participants