I'm sure we've all noticed that x.max(4) doesn't behave quite intuitively: at first glance it looks like "x, but no more than 4" when it's in fact the opposite. Same for .min, of course. So what would people think about a function with a little more evocative syntax?
x.limit(y..) equal to x.max(y)
x.limit(..=y) equal to x.min(y)
x.limit(y..=z) equal to x.clamp(y, z)
Could also go with range.limit(value), but the point of this function is to make function chains more convenient and intuitive, so that wouldn't really work.
EDIT: Actually, that might not be what you want for floats, since it'll give f32::MAX, not f32::INFINITY, and thus there's still a comparison involved.
The classic problem with "well, what does 'max' really mean, anyway?", since the trait method can return a value that's smaller than one of the other values...
I agree it'd be nice to have the two unary variants of clamp -- I've wanted this multiple times in just the last few months. Writing it out with clamp and Ty::min/max is verbose and typically requires repeating the type.
From what I could find, only x..=y was considered, not general ranges. It was discarded because inclusive range syntax was not stable yet at the time, not because it would be less ergonomic or so. So while a range-based design was considered, nothing like @Kyuuhachi's proposal was considered during the RFC.
It might be worth filing an ACP for limit? The range-based API looks really nice.
IIRC part of this is that supporting x..y would mean that it would also need some extra support like "get the largest value less than y" that we don't have traits for today. And if it takes impl RangeBounds then it would need the trait bound for next_up and next_down even if you pass x.. that wouldn't use them.
So I think it was mostly a "well we can do the simple thing and not have to add a new trait" decision.
(Maybe today a sealed trait for just the couple of types that are "easy" would be fine, though? Dunno.)
As someone who ran into this issue recently (such a silly mistake) I'd be glad to write out the coercion as a range type instead of having to remember the 2-argument function T::min(a, b) behavior applies when using it as a method a.min(b). It's such an easy thing to internalize, yet i find it's also easy to just get it wrong without thinking deeply enough.
(Imagining it as a #[cfg(random 30%)] use T::{min as max, max as min}; though this doesn't fully work, it illustrates the struggle)
Not sure if this has already been mentioned, but for those unaware: std::cmp::min and std::cmp::max exist. They're generic so you don't need to write it as u32::max(a, b) or whatever.