~= operator in Swift

29,175

Solution 1

It is an operator used for pattern matching in a case statement.

You can take a look here to know how you can use and leverage it providing your own implementation:

Here is a simple example of defining a custom one and using it:

struct Person {
    let name : String
}

// Function that should return true if value matches against pattern
func ~=(pattern: String, value: Person) -> Bool {
    return value.name == pattern
}

let p = Person(name: "Alessandro")

switch p {
// This will call our custom ~= implementation, all done through type inference
case "Alessandro":
    print("Hey it's me!")
default:
    print("Not me")
}
// Output: "Hey it's me!"

if case "Alessandro" = p {
    print("It's still me!")
}
// Output: "It's still me!"

Solution 2

Simply use a shortcut to "range": you can construct a range and "~=" means "contains". (other can add more theoretical details, but the sense is this). Read it as "contains"

let n: Int = 100

// verify if n is in a range, say: 10 to 100 (included)

if n>=10 && n<=100 {
    print("inside!")
}

// using "patterns"
if 10...100 ~= n {
    print("inside! (using patterns)")

}

try with some values of n.

Is used widely for example in HTTP response:

if let response = response as? HTTPURLResponse , 200...299 ~= response.statusCode {
                let contentLength : Int64 = response.expectedContentLength
                completionHandler(contentLength)
            } else {
                completionHandler(nil)

Solution 3

You can look into Define Swift

func ~=<I : IntervalType>(pattern: I, value: I.Bound) -> Bool
func ~=<T>(lhs: _OptionalNilComparisonType, rhs: T?) -> Bool
func ~=<T : Equatable>(a: T, b: T) -> Bool
func ~=<I : ForwardIndexType where I : Comparable>(pattern: Range<I>, value: I) -> Bool
Share:
29,175
Fogmeister
Author by

Fogmeister

iOS Developer in Leeds.

Updated on July 05, 2022

Comments

  • Fogmeister
    Fogmeister almost 2 years

    I recently downloaded the Advanced NSOperations sample app from Apple and found this code...

    // Operators to use in the switch statement.
    private func ~=(lhs: (String, Int, String?), rhs: (String, Int, String?)) -> Bool {
        return lhs.0 ~= rhs.0 && lhs.1 ~= rhs.1 && lhs.2 == rhs.2
    }
    
    private func ~=(lhs: (String, OperationErrorCode, String), rhs: (String, Int, String?)) -> Bool {
        return lhs.0 ~= rhs.0 && lhs.1.rawValue ~= rhs.1 && lhs.2 == rhs.2
    }
    

    It seems to use the ~= operator against Strings and Ints but I've never seen it before.

    What is it?

  • Fogmeister
    Fogmeister almost 8 years
    That's good to know but doesn't actually explain what it is. What makes it equate to true or false? I can see from this that it can be run against to Equatable variables of the same type but this tells me nothing of the implementation.
  • Robo Robok
    Robo Robok over 6 years
    Is switch statement the only place where this operator is being used? How about for case let item in items {...} etc.?
  • Alessandro Orrù
    Alessandro Orrù over 6 years
    @RoboRobok you are perfectly right, it will work in any case statement. I updated my post.
  • mfaani
    mfaani over 6 years
    CAUTION: That which you switch over must be the 2nd parameter of your ~= overload. Meaning func ~=(value: Person, pattern: String) -> Bool{ return value.name == pattern } would generate this error: expression pattern of type 'String' cannot match values of type 'Person'
  • Alessandro Orrù
    Alessandro Orrù over 6 years
    @Honey yes, it's a bit counter intuitive. The pattern is the 1st parameter, while the value to match against is the 2nd. My example is correct, though.
  • mfaani
    mfaani over 6 years
    Yes your example is correct. This was new to me and I was just messing around with the ~= and changing the parameter names and didn't run into any issues. But if I swapped the position of the types then I ran into an issue. Not sure if this is correct, but it seems that it somehow ignores the parameter 'names' and only refers to them using their 'position' in the function signature. I'm wondering how Swift is pulling this off.
  • Alessandro Orrù
    Alessandro Orrù over 6 years
    @Honey This is normal, Swift requires to keep the parameters in the order of the function signature.
  • Mark A. Donohoe
    Mark A. Donohoe about 6 years
    Just a comment... you can actually override the ~= in both directions (i.e. person on left, string on right and vice-versa.) Doing that lets you match in any order and solves the problem mentioned in the comment above.
  • Alessandro Orrù
    Alessandro Orrù about 6 years
    @MarqueIV this is true only if you want to explicitly use the operator in your code. Doing what you say has no effect for case statements
  • Mark A. Donohoe
    Mark A. Donohoe about 6 years
    @AlessandroOrrù, that's actually not true. I just took your above code, wrote the second version that made Person the pattern and String the value, then in the switch, I did 'switch 'Alessandro' with a 'case p' and it matched just fine. The only requirement is you have a version of the '~=' with the correct pattern and value types to test against. Then you can use it in the switch-case statement or anywhere else.
  • Alessandro Orrù
    Alessandro Orrù about 6 years
    @MarqueIV well, this of course works, but doesn't solve the problem mentioned in the comments above. The problem was that it is confusing that to match switch p .... case "Alessandro" you have to write func ~=(pattern: String, value: Person) instead of func ~=(value: Person, pattern: String). The ~= needs the pattern as first param, not the value, and that's the source of confusion for many users. Read again what Honey wrote and you see my point ;) .
  • Mark A. Donohoe
    Mark A. Donohoe about 6 years
    But the comments really don't talk about a problem with pattern matching. They talk about a problem where they incorrectly thought they could reorder parameters, which of course isn't the case. That's a Swift language limitation that's shared by a lot of others. My point about my comment above was simply that whenever you create one pattern-matching ~= function, it's a good idea to do the reverse since you can then match in any direction you want.
  • Alessandro Orrù
    Alessandro Orrù about 6 years
    @MarqueIV yes, I agree that this gives you more flexibility. But in your original comment you wrote solves the problem mentioned in the comment above that is not true. Or, better, it solves the problem by writing all the possible permutations, so that one of them is for sure the correct version, even if the other one is potentially never needed.
  • Mark A. Donohoe
    Mark A. Donohoe about 6 years
    But it does solve the problem specifically mentioned in the comment. If you added that reverse function, they would not be seeing the error they were saying they did when they switched things around
  • Hadži Lazar Pešić
    Hadži Lazar Pešić over 4 years
    Good explanation!
  • Peter Schorn
    Peter Schorn about 4 years
    It returns true if the item is is range range, and false if it is not. For example, 0...10 ~= 5 is true because 5 is in the range from 0 to 10. 0...10 ~= 50 is false because 50 is not in the range.