Skip to content

Commit 8c74eb0

Browse files
committed
fix: properly handle partial ordering in BoundPair constructor
This PR adds tests which highlighted a bug. The BoundPair::new() constructor was not correctly handling partially ordered types, particularly floating point NaN values. Changed the implementation to explicitly use partial_cmp instead of comparison operators, ensuring correct behavior with: - NaN floating point values - Custom partially ordered types - Infinite values - Regular numeric comparisons The new implementation satisfies Clippy's neg-cmp-op-on-partial-ord lint and makes the partial ordering handling explicit. Tests updated to verify correct behavior with floating point edge cases.
1 parent a47b2b5 commit 8c74eb0

File tree

1 file changed

+72
-4
lines changed

1 file changed

+72
-4
lines changed

src/bound_pair.rs

Lines changed: 72 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,9 @@ where
6060
/// assert_eq!(BoundPair::new(2.0, 2.0), None);
6161
/// ```
6262
pub fn new(left: T, right: T) -> Option<BoundPair<T>> {
63-
if left >= right {
64-
None
65-
} else {
66-
Some(BoundPair { left, right })
63+
match left.partial_cmp(&right) {
64+
Some(std::cmp::Ordering::Less) => Some(BoundPair { left, right }),
65+
_ => None,
6766
}
6867
}
6968

@@ -99,3 +98,72 @@ where
9998
&self.right
10099
}
101100
}
101+
102+
#[cfg(test)]
103+
mod tests {
104+
use super::*;
105+
106+
#[test]
107+
fn test_valid_creation() {
108+
assert!(BoundPair::new(1, 2).is_some());
109+
assert!(BoundPair::new(-1.0, 1.0).is_some());
110+
assert!(BoundPair::new(0u32, 100u32).is_some());
111+
}
112+
113+
#[test]
114+
fn test_invalid_creation() {
115+
assert!(BoundPair::new(2, 1).is_none());
116+
assert!(BoundPair::new(1.0, 1.0).is_none());
117+
assert!(BoundPair::new(100u32, 50u32).is_none());
118+
}
119+
120+
#[test]
121+
fn test_accessors() {
122+
let bp = BoundPair::new(1.5, 2.5).unwrap();
123+
assert_eq!(*bp.left(), 1.5);
124+
assert_eq!(*bp.right(), 2.5);
125+
}
126+
127+
#[test]
128+
fn test_floating_point() {
129+
assert!(BoundPair::new(f64::NEG_INFINITY, f64::INFINITY).is_some());
130+
assert!(BoundPair::new(f64::NAN, 1.0).is_none());
131+
assert!(BoundPair::new(1.0, f64::NAN).is_none());
132+
}
133+
134+
#[test]
135+
fn test_copy() {
136+
let bp1 = BoundPair::new(1, 2).unwrap();
137+
let bp2 = bp1;
138+
assert_eq!(bp1, bp2);
139+
}
140+
141+
#[cfg(feature = "serde")]
142+
mod serde_tests {
143+
use super::*;
144+
use serde_json;
145+
146+
#[test]
147+
fn test_serialize() {
148+
let bp = BoundPair::new(1, 2).unwrap();
149+
let serialized = serde_json::to_string(&bp).unwrap();
150+
assert_eq!(serialized, r#"{"left":1,"right":2}"#);
151+
}
152+
153+
#[test]
154+
fn test_deserialize() {
155+
let json = r#"{"left":1,"right":2}"#;
156+
let bp: BoundPair<i32> = serde_json::from_str(json).unwrap();
157+
assert_eq!(*bp.left(), 1);
158+
assert_eq!(*bp.right(), 2);
159+
}
160+
161+
#[test]
162+
fn test_roundtrip() {
163+
let bp1 = BoundPair::new(1.5, 2.5).unwrap();
164+
let serialized = serde_json::to_string(&bp1).unwrap();
165+
let bp2: BoundPair<f64> = serde_json::from_str(&serialized).unwrap();
166+
assert_eq!(bp1, bp2);
167+
}
168+
}
169+
}

0 commit comments

Comments
 (0)