Hello Swift Community.
During discussion of clamped function Add a `clamp` function to Algorithm.swift, it was found an interesting case with Ranges initialization.
func doSomething(val1: Double, val2: Double) {
let offset = 10.clamped(to: val1...val2)
}
If val1 > val2, the app will crash with error "Can't form Range with upperBound < lowerBound"
To prevent this, we are forced to check that val1 < val2 every time, which is annoying.
let offset = 10.clamped(to: val1 <= val2 ? val1...val2 : val2...val1)
There also other situations, where Range bounds come from function arguments and because of that can't be checked at compile time:
func validatePaymentAmount(_ amount: Int, minAmount: Int, maxAmount: Int) -> ValidationResult {
let validRange = ClosedRange(uncheckedBounds: (minAmount, maxAmount))
}
func didEndEditing(text: String) {
let validator = SymbolsCountValidator(validCount: ClosedRange(uncheckedBounds: (lowerBound, upperBound))
}
let horizontalArea = ClosedRange(uncheckedBounds: (view1.frame.origin.x, view2.frame.origin.x)
Proposed solution
The idea is to add new initializer to Range, thanks to @benrimmington
extension ClosedRange {
init(unorderedBounds: (leftBound: Bound, rightBound: Bound)) {
let (leftBound, rightBound) = unorderedBounds
let bounds: (lower: Bound, upper: Bound) = (leftBound <= rightBound ? (leftBound, rightBound) : (rightBound, leftBound))
self.init(uncheckedBounds: bounds)
}
}
Source compatibility
This change is purely additive and should not affect source compatibility.
Effect on ABI stability
No known effect.
Effect on API resilience
No known effect.