SoFunction
Updated on 2025-04-10

Detailed explanation of the tutorial on type conversion and pattern matching in Swift 5.1

Type conversion is used in SwiftisandasOperator implementation.

Type Check

Use operatorsisChecks whether an instance is a certain class and its inheritance system's parent or child class type. If it is a certain type of class (the parent or child class of the inheritance system), it will returntrue, otherwise returnfalse

class Cat {
 func hairColor() -> String {
  return "Colorful"
 }
}
class WhiteCat: Cat {
 override func hairColor() -> String {
  return "White"
 }
}
class BlackCat: Cat {
 override func hairColor() -> String {
  return "black"
 }
}
// Must comply with the `Cat` class and its subclasses, type inference requireslet kinds = [WhiteCat(),BlackCat(),WhiteCat(),WhiteCat()]
for item in kinds {
 if item is WhiteCat {
  print("White Cat")//!< 3 times }
 if item is BlackCat {
  print("Black Cat")//!< 1 time }
 if item is Cat {
  print("cat")//!< 4 times }
}

Convert down

A constant or variable of a certain class type may actually be an instance of its subclass. In this case, we will use the type conversion operator (as?oras!) Convert to subclass type.

as?: The conditional form of type conversion. When downconverting to a certain type, the optional value of that type is returned, that is, when the conversion fails, it returnsnil. Use scenario: A situation where down conversion may fail.

as!: The forced form of type conversion. When downconverting to a certain type, a forced unpacking will be performed, that is, a runtime error is triggered when the conversion fails. Use scenario: Converting downward will not fail

// Must comply with the `Cat` class and its subclasses, type inference requireslet kinds = [WhiteCat(),BlackCat(),WhiteCat(),WhiteCat()]
for item in kinds {
 if let white = item as? WhiteCat {
  print("Hair:\(())")
 }
 if let black = item as? BlackCat {
  print("Hair:\(())")
 }
}

The following content is summarized fromApple's official blog

Before Swift 1.2asOperators can perform two different types of conversions: guaranteed conversion and cast conversion.

Guaranteed Conversion: Ensure that one type of value is converted to another type, which is guaranteed to be validated at compile time by the compiler.

For example:

• Upcasting, converting the value of the current type to one of the parent classes of that type.

• Specify the type of number:let num = 6 as Float

**Captive: **Forcing the value of one type to another, this conversion compiler cannot guarantee security and may trigger a runtime error.

For example: the above-mentioned downcasting converts a type of value into one of its subclasses. Ensure that the conversion is still used after Swift 1.2asoperator, but castas!Operator.

AnyandAnyObjectType conversion

Swift provides two special types to handle non-specific types:

any
AnyObject

In some useanyandAnyObjectIn special scenarios,AnyandAnyObjectThe instances represented require the knowledge of pattern matching such as type conversion patterns, value binding patterns, expression patterns, etc. So let's first introduce the mode in Swift.

Type conversion mode

There are two modes for type conversion:isMode andasmodel.isMode onlyswitchStatementcaseUsed in the tag.isMode andasThe mode has the following form:

 

is &lt;#Type#&gt;
//pattern: It means that a pattern is also needed here.&lt;#pattern#&gt; as &lt;#Type#&gt;

is mode: If the type of the runtime value is the same as the type specified to the right of the is mode or the subclass of that type, the is mode matches this value. This behavior is very suitable for case scenarios of switch statements. The is pattern behaves similarly to the is operator because they both perform type conversion but discard the returned type after the type conversion.

as mode: If the type of the value at runtime is the same as the type specified to the right of the as mode or the subclass of that type, the as mode matches this value. If the match is successful, the type of the matched value will be converted to the type specified to the right of the as pattern.

Value binding mode

The value binding pattern binds the matching value to a variable or constant. Bind the matching value to a constant, and the binding pattern starts with the let keyword; bind to a variable starts with the var keyword.

let point = (3,2)
switch point {
case let(x,y):
 //The value binding pattern matches X value: 3, Y value: 2 print("The value binding pattern matchesXvalue:\(x),Yvalue:\(y)")
}

Wildcard pattern

Wildcard pattern matches and ignores any value and is represented by an underscore _ .

for _ in 1...9 {
 print("Wildcard pattern")
}

Identifier mode

The identifier pattern matches any value and binds the matching value to the name of the variable or constant.

let someValue = 42

someValue is an identifier pattern that matches the value 42 of type Int. The match is successful, 42 will be assigned to the constant someValue. When the pattern to the left of a variable or constant declaration is an identifier pattern, the identifier pattern is implicitly a sub-mode of the value binding pattern. #### Tuple Pattern Tuple Pattern is a comma-separated list of zero or more elements enclosed in parentheses. Tuple pattern matches the value of the corresponding tuple type.

The brackets around the tuple pattern containing a single element are invalid. This pattern matches the value of the individual element type. So the following is equivalent:

let a = 2  // a: Int = 2
let (a) = 2  // a: Int = 2
let (a): Int = 2 // a: Int = 2

Enumeration Case pattern

Enumeration Case pattern matches case exists in an existing enum. The enumeration Case pattern appears in the case tag of the switch statement and in the if , while , guard , for - in statements.

If the enum case attempting to match has an associated value, the corresponding enum Case pattern must specify the tuple corresponding to each associated value.

enum VendingMachineError {
 case InvalidGoods//!< Product is invalid case StockInsufficient//!< Insufficient inventory case CoinInsufficient(coinNeeded:Int,caseDes:String)
}
let enumArray = [(coinNeeded: 4, caseDes: "Vending machine, insufficient coins, please add"),
     .InvalidGoods,
     .StockInsufficient,
     .CoinInsufficient(coinNeeded: 6, caseDes: "Vending machine, insufficient coins, exceeding limit")]
for patternCase in enumArray {
 switch patternCase {
 case .CoinInsufficient(coinNeeded: let x, caseDes: let y) where x &gt; 5:
  print(x,y)
 case let .CoinInsufficient(coinNeeded: x, caseDes: y):
  print(x,y)
 case .InvalidGoods:
  print("The product is invalid")
 default:
  print("Not matched")
 }
}

The enumeration Case pattern also matches options for the enumeration type. When the optional Optional is an enum type, .none and .some can appear in the same switch statement as other cases of the enum type. This simplified syntax allows us to omit optional modes.

enum SomeEnum { case left, right,top,down}
let array : Array&lt;SomeEnum?&gt; = [.left,nil,.right,.top,.down]
//Method 1: { (item) in
 switch item {
 case .left?:
  print("Left")
 case ?:
  print("right")
 case .down?:
  print("Down")
 case .top?:
  print("superior")
 default:
  print("No value")
 }
}
//Method 2: { (item) in
 switch item {
 case .some(let x):
  print("Right to optional optionsitemUnpacking to obtain:\(x)")//!&lt; left,right,top,down
 case .none:
  print("No value") //nil
 }
}

Optional mode

Optional pattern matches the value contained in the Optional<Wrapped> enumeration (this is the implementation principle of optionality): some(Wrapped). That is, when matching the optional options have values.

public enum Optional<Wrapped> : ExpressibleByNilLiteral {
 /// The absence of a value.
 /// In code, the absence of a value is typically written using the `nil`
 /// literal rather than the explicit `.none` enumeration case.
 case none
 /// The presence of a value, stored as `Wrapped`.
 case some(Wrapped)
 ......
}

The optional mode consists of an identifier mode followed by ? and appears in the same position as the enumeration Case mode. Because the optional mode is the Case pattern syntax sugar enumerated by the Optional<Wrapped>. So the following two writing methods are equivalent:

let someInt : Int? = 42
//Method 1: Enumerate case modeif case let .some(x) = someInt {
 print(x)
}
if case .some(let x) = someInt {
 print(x)
}
//Method 2: Optional modeif case let x? = someInt {
 print(x)
}

It is convenient to iterate over an array containing options using optional patterns:

enum SomeEnum { case left, right,top,down}
let array : Array<SomeEnum?> = [.left,nil,.right,nil,.top,.down]
for case let item? in array {
 print(item)//!< log:left right top down
}
for case let .some(item) in array {
 print(item)//!< log:left right top down
}
for case .some(let item) in array {
 print(item)//!< log:left right top down
}

Expression pattern

Expression pattern: represents the value of the expression, which only appears in the case tag of the switch statement.

Mechanism of expression pattern: Use the ~= operator in the Swift standard library to compare the value of the expression in the expression pattern with the matching value (input value). If ~= returns true, it proves that the match is successful, otherwise the match fails.

The ~= operator uses the == operator by default to compare two values ​​of the same type; you can also match the range value by checking whether a value is within a certain range.

let point = (9,14)
switch point {
case (9,14):
 print("Expression pattern usage`~=`Accurate matching::(\(point.0),\(point.1))")
 fallthrough
case (5..&lt;10,0...20):
 print("Expression pattern usage`~=`Range matching:(\(point.0),\(point.1))")
default:
 print("Not matched")
}

The ~= operator can be overloaded to provide custom expression matching behavior:

//Global statement: class external, otherwise an error will be reportedfunc ~= (pattern: String, value: Int) -&gt; Bool {
 return pattern == "\(value)"
}
let point = (9,14)
switch point {
case ("9","14")://If you don't overload, an error will be reported print("Expression pattern usage`~=`Accurate matching:(\(point.0),\(point.1))")
 fallthrough
case (5..&lt;10,0...20):
 print("Expression pattern usage`~=`Range matching:(\(point.0),\(point.1))")
default:
 print("Not matched")
}

After introducing the mode, let’s give an example to illustrate the use of the mode type conversion in Any and AnyObject. Example 1:

var things : [Any] = [0, 0.0, 42, 3.14159, "hello", (3.0, 5.0),
      WhiteCat(),{ (name: String) -&gt; String in "Hello, \(name)" } ]
for thing in things {
 switch thing {
 case 0 as Int:
  print("`as`Pattern matching two parts,pattern:Expression pattern(`0`),type:Match type(`Int`),Match results:0")
 case (0) as Double:
  print("`as`Pattern matching two parts,pattern:Expression pattern(`0`),type:Match type(`Double`),Match results:0.0")
 case is Double:
  print("`is` pattern matches the value of type `Double`, and the value type is the same as the type and subclass on the right of `is`, execute this sentence")
 case let someInt as Int:
  print("`as`Pattern matching two parts,pattern:Value binding mode(`let someInt`),type:Match type(`Int`),Match results:\(someInt)")
 case _ as Int:
  print("`as`Pattern matching two parts,pattern:Wildcard pattern(`_`),type:Match type(`Int`),Match results被忽略")
 case let someDouble as Double where someDouble &gt; 0:
  print("`as`Pattern matching two parts,pattern:Value binding mode(`let someDouble`),type:Match type(`Double`),Match results:\(someDouble)")
 case let someString as String:
  print("`as`Pattern matching two parts,pattern:Value binding mode(`let someString`),type:Match type(`String`),Match results:\(someString)")
 case let (x, y) as (Double, Double):
  print("`as`Pattern matching two parts,pattern:Tuple pattern(`let (x, y) `),type:Match type(Tuples`(Double, Double)`),Match results:\((x, y))")
  fallthrough
 case (2.0...4.0, 3.0...6.0) as (Double, Double):
  print("`as`Pattern matching two parts,pattern:Expression pattern(`(2.0...4.0, 3.0...6.0) `),type:Match type(Tuples`(Double, Double)`))")
 case let cat as WhiteCat:
  print("`as`Pattern matching two parts,pattern:Value binding mode(`let cat`),type:Match type(Object`WhiteCat`),Match results:\(cat)")
 case let sayHelloFunc as (String) -&gt; String:
  print("`as`Pattern matching two parts,pattern:Value binding mode(`let sayHelloFunc`),type:Match type(function`(String) -&gt; String`),Match results:\(sayHelloFunc("QiShare"))")
 default:
  print("Other results, not matched")
 }
}

Example 2:

let point = (9,14)
switch point {
case (9,14):
 print("Expression pattern usage`~=`Accurate matching::(\(point.0),\(point.1))")
 fallthrough
case (5..&lt;10,0...20):
 print("Expression pattern usage`~=`Range matching:(\(point.0),\(point.1))")
default:
 print("Not matched")
}

References:Swift 5.1 Official Programming Guide

Summarize

This is the article about the detailed explanation of the tutorial on Swift 5.1's type conversion and pattern matching. For more related content on Swift type conversion and pattern matching, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!