Footguns
Normalized Conversions
Most of the time normalization is transparent to the user, but it is a potential source of error, particularly when converting types that have implicit open bounds.
#![allow(unused)] fn main() { use intervalsets_core::prelude::*; let discrete = EnumInterval::open(0, 10); assert_eq!(discrete.lval(), Some(&1)); assert_eq!(discrete.rval(), Some(&9)); assert_eq!(discrete, (0, 10).into()); assert_eq!(discrete, [1, 9].into()); }
Floating Point Types
Making Ord
a trait bound for most APIs would eliminate a whole class of errors
(TotalOrderError), but floats come with a whole host of complexities regardless.
NAN
is not part of the default ordering, though there is atotal_cmp
available now.- rounding error can cause issues with testing values near a finite bound.
FiniteBound(f32::INFINITY)
andFiniteBound(f32::NEG_INFINITY)
are both valid syntax, though all manner of headache inducing semantically speaking.
Sometimes, floats are still the right tool for the job, and it is left to the
user to choose the right approach for the given problem. Fixed precision
decimal types like rust_decimal
do side step some pitfalls.
Fallibility
#![allow(unused)] fn main() { use intervalsets_core::prelude::*; let x = FiniteInterval::open(1.0, 0.0); assert_eq!(x, FiniteInterval::empty()); // total order error -> panic // infallible for properly implemented [`Ord`] types. let result = std::panic::catch_unwind(|| { FiniteInterval::open(f32::NAN, 0.0); }); assert!(result.is_err()); let x = FiniteInterval::strict_open(f32::NAN, 0.0); assert_eq!(x.is_err(), true); let x = FiniteInterval::strict_open(1.0, 0.0); assert_eq!(x.unwrap(), FiniteInterval::empty()); }
Silent failures can make it difficult to isolate logic errors as they are able to propogate further from their source before detection.
#![allow(unused)] fn main() { use intervalsets_core::prelude::*; let interval = EnumInterval::closed(0, 10); let oops = interval .with_left_closed(20) // empty here .with_right(None); assert_ne!(oops, EnumInterval::closed_unbound(20)); assert_eq!(oops, EnumInterval::empty()); let fixed = interval.with_right(None).with_left_closed(20); assert_eq!(fixed, EnumInterval::closed_unbound(20)); }