[swift-users] How to be DRY on ranges and closed ranges?
Dave Abrahams
dabrahams at apple.com
Sun Oct 16 17:25:28 CDT 2016
on Wed Oct 12 2016, Jean-Denis Muys <swift-users-AT-swift.org> wrote:
> Hi,
>
> I defined this:
>
> func random(from r: Range<Int>) -> Int {
> let from = r.lowerBound
> let to = r.upperBound
>
> let rnd = arc4random_uniform(UInt32(to-from))
> return from + Int(rnd)
> }
>
> so that I can do:
>
> let testRandomValue = random(from: 4..<8)
>
> But this will not let me do:
>
> let otherTestRandomValue = random(from: 4...10)
>
> The error message is a bit cryptic:
>
> “No ‘…’ candidate produce the expected contextual result type ‘Range<Int>’”
>
> What is happening is that 4…10 is not a Range, but a ClosedRange.
>
> Of course I can overload my function above to add a version that takes a ClosedRange.
>
> But this is not very DRY.
>
> What would be a more idiomatic way?
In our original design we had a common protocol to which open and closed
ranges conformed, and you could pass an instance of RangeProtocol. That
was removed because the overall complexity was not a win, but I predict
it will come back for Swift 4:
https://github.com/apple/swift/pull/3737
But for what you want to do, you don't need anything that complicated:
protocol CompleteRange {
associatedtype Bound : Comparable
var lowerBound : Bound { get }
var upperBound : Bound { get }
}
extension CountableRange : CompleteRange {}
extension CountableClosedRange : CompleteRange {}
import Darwin
func random<R: CompleteRange>(from r: R) -> Int where R.Bound == Int, R: Collection {
let rnd = arc4random_uniform(numericCast(r.count))
return r.lowerBound + Int(rnd)
}
print(random(from: -5..<5), random(from: -20...20))
Hope this helps,
--
-Dave
More information about the swift-users
mailing list