Reputation: 15286
I want to create a function which has a number parameter that should be between 0..100 %
I thought that the best way to enforce this would be by creating a wrapper type using FloatingPointType
protocol , but I am getting a compilation error
Protocol 'FloatingPointType' can only be used as a generic constraint because it has Self or associated type requirements
struct Percent {
init(val : FloatingPointType) {
// enforce value is between 0..100
}
}
func hideView(percent : Percent) {
// percent is 0..100 at this point
.. do some work here
}
What would be the correct way to enforce this condition at compile time?
Upvotes: 3
Views: 943
Reputation: 539965
Update: As of Swift 5.1 this can more easily achieved with “property wrappers”, see for example “Implementing a value clamping property wrapper” on NSHipster.
The easiest way would be to define a type that holds a
Double
(or Float
or Int
) in the required range:
struct P {
let val : Double
init (val : Double) {
// ...
}
}
But if you want to treat different floating point types then you have to define a generic class
struct Percent<T : FloatingPointType> {
let val : T
init(val : T) {
self.val = val
}
}
To compare the values you need to require Equatable
as well:
struct Percent<T : FloatingPointType where T: Equatable> {
let val : T
init(val : T) {
if val < T(0) {
self.val = T(0)
} else if val > T(100) {
self.val = T(100)
} else {
self.val = val
}
}
}
Example:
let p = Percent(val: 123.4)
println(p.val) // 100.0
Note that this requires that hideView()
is generic as well:
func hideView<T>(percent : Percent<T>) {
// percent.val has the type `T` and is in the range
// T(0) ... T(100)
}
Upvotes: 4
Reputation: 1394
Adding up to Martin's answer with updating for Swift 5:
struct Percentage<T: FloatingPoint> {
let value: T
init(value: T) {
self.value = min(max(value, T(0)), T(100))
}
}
Usage site, you can define the generic type like:
func updateCircleWith(percentage: Percentage<CGFloat>) {
// percentage.value will be between 0 and 100 here
// of course the CGFloat(0) and CGFloat(100)
}
Upvotes: 0
Reputation: 131471
It sounds like you're trying to enforce, at compile time, that you can't pass a value outside the range 0.0 to 100.0 to a function. You can't do that.
What you can do is write your function to throw an exception if it is passed a value that's out of range, or display an error to the user and return if it's out of range.
Upvotes: 1
Reputation: 38190
The language feature you are looking for is called Partial Functions. A partial function is a function that is not defined for all possible arguments of the specified type. For instance, they are available in Haskell or Scala - but they are not available in Swift.
So the best you can do is to check at runtime if the provided value lies within the valid range and act accordingly (e.g. raise an exception or return an error).
Upvotes: 2