Programming in Swift: Functions & Types, Episode 24: More Switch Statements | raywenderlich.com

You can use switch statements to switch on types other than enumerations! Try it out in this episode.


This is a companion discussion topic for the original entry at https://www.raywenderlich.com/28433240-programming-in-swift-functions-types/lessons/24

Hi, really enjoying the course so far! I have a question about using ranges in switch statements. I see that in the below function, the range 1…9 returns a valid set of cases.

func getDescription(for number: Int) -> String {
    switch number {
    case 0:
        return "Zero"
    case 1...9:
        return "Between 1 and 9"
    case let negativeNumber where negativeNumber < 0:
        return "Negative"
    default:
        return "No description"
    }
}

But when I try to use stride(from: 1, through: 9, by: +2) to get odd numbers between 1 and 9, it gives me an error:

Expression pattern of type ‘StrideThrough’ cannot match values of type ‘Int’

But this works just fine:

for i in stride(from: 1, through: 9, by: +2) {
    print(i)
}

So my question is why does stride(through:) work in for loops but not in switch statements?

Thanks,

Jake

1 Like

And having watched 30s more of the video now I see that using number % 2 would work better here, but I’m still curious about the stride issue!

@thewildjacko The stride(from:through:by:) function returns a StrideThrough<Int> sequence which works exactly like a range does with for in loops but not in switch statements.

The stride(from:to:by:) function returns a StrideTo<Int> sequence which works exactly like a range does with for in loops but not in switch statements.

The switch statement uses the expression pattern under the hood to implement custom pattern matching. You need to overload the pattern matching operator in this case so that it works with both StrideThrough<Int> and StrideTo<Int> sequences like this:

func ~=(lhs: StrideThrough<Int>, rhs: Int) -> Bool {
  for value in lhs {
    if value == rhs {
      return true
    }
  }
  return false
}

func ~=(lhs: StrideTo<Int>, rhs: Int) -> Bool {
  for value in lhs {
    if value == rhs {
      return true
    }
  }
  return false
}

The first overloaded version of the ~= function takes a StrideThrough<Int> parameter and an Int one. It returns true if the lhs sequece contains the rhs value and false otherwise.

The second overloaded version of the ~= function takes a StrideTo<Int> parameter and an Int one. It returns true if the lhs sequece contains the rhs value and false otherwise.

You can manually use the pattern matching operator’s custom implementation with both StrideThrough<Int> and StrideTo<Int> sequences as follows:

stride(from: 0, through: 8, by: 2) ~= 4 // true
stride(from: 1, to: 10, by: 2) ~= 5 // true

Both of the above lines of code return true because both of the even and odd StrideThrough<Int> and StrideTo<Int> sequences contain the corresponding even and odd Int values in this case.

You can also use the pattern matching operator with ranges by default like this:

0...9 ~= 4 // true
0..<10 ~= 5 // true
0... ~= 4 // true
...0 ~= -4 // true
..<0 ~= -5 // true

All of the above lines of code return true because all of the closed, half open and one sided ranges contain the corresponding Int values in this case. This is exactly why you can actually use ranges in switch statements by default.

You can now work with both StrideThrough<Int> and StrideTo<Int> sequences inside switch statements too as follows:

func getDescription(for number: Int) -> String {
  switch number {
    case let negative where negative < 0: return "Negative number"
    case stride(from: 0, through: 8, by: 2): return "Even digit"
    case stride(from: 1, to: 10, by: 2): return "Odd digit"
    case let even where even % 2 == 0: return "Even number"
    case let odd where odd % 2 == 1: return "Odd number"
    default: return "No description"
  }
}

getDescription(for: 4) // "Even digit"
getDescription(for: 5) // "Odd digit"

You use the pattern matching operator’s custom implementation to check if a certain digit is either even or odd in this case.

Please let me know if you have any other questions or more issues about the whole thing when you get a chance. Thank you!

2 Likes

Thanks so much—this makes total sense!

2 Likes

Hi, Catie. I’m a beginner in Swift. I just took the course and I’m wondering what does .max indicate here?

func getDescription(for number: Int) -> String {
  ...
  case _ where number > .max / 2:
    return "very large"
  ...
}

I tried print(.max) in the getDescription function and it throws " type ‘Any’ has no member ‘max’.

func getDescription(for number: Int) -> String {
  print(.max)
  ...
}

I then tried print(number.max) in the getDescription function and it throws static member ‘max’ cannot be used on instance of type ‘Int’.

func getDescription(for number: Int) -> String {
  print(number.max)
  ...
}

I also tried print(.max) and print(number.max) in the case but still the same.

func getDescription(for number: Int) -> String {
  ...
  case _ where number > .max / 2
    print(.max)
    print(number.max)
    return "very large"
  ...
}

What does .max mean? Why it works okay in case _ where number > .max / 2: but not other places in the function?

Hi! Thanks for your question.

You were very close to figuring this out yourself! In the switch statement, type inference is assuming an Int in front of the .max.

max is a type property of Int. You might also hear this referred to as a “static property”. You’ll learn more about these kinds of properties (and methods!) later in the course. Briefly, type properties are special properties defined on a type, as opposed to an instance of a type. You’ll find a lot of Swift types have type properties set up to store handy constants or computed properties that wouldn’t make sense as a property of a particular instance, but apply to the type in general.

1 Like

Hi, Catie. Thank you for your reply. So it’s because we set number as an Int, base on the context, the compiler infer that .max means Int.max?

Because when I try these, I get errors like:

.max 
// reference to member `max' cannot be resolved without a contextual type

print(.max) 
// type 'Any' has no member 'max'