Skip to content

Commit 9fad211

Browse files
authored
Merge pull request #1961 from Xaeroxe/Xaeroxe-clamp-rfc
Add clamp RFC
2 parents a7cd910 + ac69333 commit 9fad211

File tree

1 file changed

+129
-0
lines changed

1 file changed

+129
-0
lines changed

text/1961-clamp.md

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
- Feature Name: clamp functions
2+
- Start Date: 2017-03-26
3+
- RFC PR: https://github.com/rust-lang/rfcs/pull/1961/
4+
- Rust Issue: https://github.com/rust-lang/rust/issues/44095
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Add functions to the language which take a value and an inclusive range, and will "clamp" the input to the range. I.E.
10+
11+
```Rust
12+
if input > max {
13+
return max;
14+
}
15+
else if input < min {
16+
return min;
17+
} else {
18+
return input;
19+
}
20+
```
21+
22+
These would be on the Ord trait, and have a special version implemented for f32 and f64.
23+
24+
# Motivation
25+
[motivation]: #motivation
26+
27+
Clamp is a very common pattern in Rust libraries downstream. Some observed implementations of this include:
28+
29+
http://nalgebra.org/rustdoc/nalgebra/fn.clamp.html
30+
31+
http://rust-num.github.io/num/num/fn.clamp.html
32+
33+
Many libraries don't expose or consume a clamp function but will instead use patterns like this:
34+
```Rust
35+
if input > max {
36+
max
37+
}
38+
else if input < min {
39+
min
40+
} else {
41+
input
42+
}
43+
```
44+
and
45+
```Rust
46+
input.max(min).min(max);
47+
```
48+
and even
49+
```Rust
50+
match input {
51+
c if c > max => max,
52+
c if c < min => min,
53+
c => c,
54+
}
55+
```
56+
57+
Typically these patterns exist where there is a need to interface with APIs that take normalized values or when sending
58+
output to hardware that expects values to be in a certain range, such as audio samples or painting to pixels on a display.
59+
60+
While this is pretty trivial to implement downstream there are quite a few ways to do it and just writing the clamp
61+
inline usually results in rather a lot of control flow structure to describe a fairly simple and common concept.
62+
63+
# Detailed design
64+
[design]: #detailed-design
65+
66+
Add the following to std::cmp::Ord
67+
68+
```Rust
69+
/// Returns max if self is greater than max, and min if self is less than min.
70+
/// Otherwise this will return self. Panics if min > max.
71+
#[inline]
72+
pub fn clamp(self, min: Self, max: Self) -> Self {
73+
assert!(min <= max);
74+
if self < min {
75+
min
76+
}
77+
else if self > max {
78+
max
79+
} else {
80+
self
81+
}
82+
}
83+
```
84+
85+
And the following to libstd/f32.rs, and a similar version for f64
86+
87+
```Rust
88+
/// Returns max if self is greater than max, and min if self is less than min.
89+
/// Otherwise this returns self. Panics if min > max, min equals NaN, or max equals NaN.
90+
///
91+
/// # Examples
92+
///
93+
/// ```
94+
/// assert!((-3.0f32).clamp(-2.0f32, 1.0f32) == -2.0f32);
95+
/// assert!((0.0f32).clamp(-2.0f32, 1.0f32) == 0.0f32);
96+
/// assert!((2.0f32).clamp(-2.0f32, 1.0f32) == 1.0f32);
97+
/// ```
98+
pub fn clamp(self, min: f32, max: f32) -> f32 {
99+
assert!(min <= max);
100+
let mut x = self;
101+
if x < min { x = min; }
102+
if x > max { x = max; }
103+
x
104+
}
105+
```
106+
107+
This NaN handling behavior was chosen because a range with NaN on either side isn't really a range at all and the function can't be guaranteed to behave correctly if that is the case.
108+
109+
# How We Teach This
110+
[how-we-teach-this]: #how-we-teach-this
111+
112+
The proposed changes would not mandate modifications to any Rust educational material.
113+
114+
# Drawbacks
115+
[drawbacks]: #drawbacks
116+
117+
This is trivial to implement downstream, and several versions of it exist downstream.
118+
119+
# Alternatives
120+
[alternatives]: #alternatives
121+
122+
Alternatives were explored at https://internals.rust-lang.org/t/clamp-function-for-primitive-types/4999
123+
124+
Additionally there is the option of placing clamp in std::cmp in order to avoid backwards compatibility problems. This is however semantically undesirable, as `1.clamp(2, 3);` is more readable than `clamp(1, 2, 3);`
125+
126+
# Unresolved questions
127+
[unresolved]: #unresolved-questions
128+
129+
None

0 commit comments

Comments
 (0)