Avba
Avba

Reputation: 15286

Swift - how to declare a method which receives a number in a range

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

Answers (4)

Martin R
Martin R

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

Okhan Okbay
Okhan Okbay

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

Duncan C
Duncan C

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

Leo
Leo

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

Related Questions