こんにちは。2023年7月からはてなのマンガアプリチームで働いています、id:fxwx23 です。
はてなに入社する前は React Native (TypeScript) や Go などを書くことが多かったため、 Swift を書くことは2021年の夏以来となります(!)。Swift を書くことを本格的に再開できることになったので、早速自分の復習も兼ねて Patterns の話を少しまとめておこうと思います。
Swift の Patterns は、 値を一致させて分解する単一の値または複合値の構造を表します。 Patterns には以下の種類があります。
- Wildcard Pattern
- Identifier Pattern
- Value Binding Pattern
- Tuple Pattern
- Enumeration Case Pattern
- Optional Pattern
- Type Casting Patterns
- Expression Pattern
今回は、 自分としても発見があった Optional Pattern に焦点をあてていきたいと思います。
Optional Pattern とは
Optional Pattern は以下のように説明されています。
An optional pattern matches values wrapped in a some(Wrapped) case of an Optional
enumeration. Optional patterns consist of an identifier pattern followed immediately by a question mark
Optional Pattern は Identifier Pattern の直後に ?
を置くことで表現され、 Optional<Wrapped>
の some(Wrapped)
ケースにラップされた値と一致させることができます。(自分はSwiftは2系から書いていましたが、このパターンは知らずに使っていました...🙈)
let someOptional: Int? = 23 if case let x? = someOptional { print(x) }
また、Optional Pattern は Optional
の Enumeration Case Pattern のシンタックスシュガーと記載されており、
optional patterns are syntactic sugar for Optional enumeration case patterns
先ほど挙げたコード例は Enumeration Case Pattern と同等と解釈することができます。
let someOptional: Int? = 23 if case .some(let x) = someOptional { print(x) }
上記の例だと、単純なので if let x = someOptional { ... }
と書くことが多いと思います。今なら SE-0345: if let
shorthand を使うでしょう。
if let someOption { ... }
では、Optional Pattern の使い所はどこにあるでしょうか。
Optional Pattern の使い所
まず、自分が Optional Pattern を調べるきっかけになった Associated Values を持つ Enumeration Case Pattern です。Optional Pattern を使う前は Associated Values が Optional
なら、取り出した上で if let
で unwrap するというようなコードを書いていましたが、
enum SomeEnum { case left(Int?) } let foo = SomeEnum.left(23) if case .left(let x) = foo, let x { print(x) // 23 }
Optional Pattern は Associated Values にも有効なので、このように書くことができます。
let foo = SomeEnum.left(23) if case .left(let x?) = foo { print(x) // 23 } // これはつまり以下と同等 if case .left(.some(let x)) = foo { print(x) }
実はこれは Patterns の説明の冒頭で説明されています。switch
文の case
ラベル、do
文の catch
句、または if
、while
、guard
、または for-in
文の case
条件で使用することができることがわかります。
These include enumeration case patterns, optional patterns, expression patterns, and type-casting patterns. You use these patterns in a case label of a switch statement, a catch clause of a do statement, or in the case condition of an if, while, guard, or for-in statement.
もう一つ 2つの Optional
の文字列の順序が必要なシーンを考えてみます。意見が分かれるところですが、両方の文字列に値がある場合に使用される Optional Pattern は、読みやすい選択肢ではないかと感じました。
func compare(_ lhs: String?, _ rhs: String?) -> ComparisonResult { switch (lhs, rhs) { case (.none, .none): return .orderedSame case (.some, .none): return .orderedDescending case (.none, .some): return .orderedAscending // 以下は case (.some(let lhs), .some(let rhs)): と同等 case (let lhs?, let rhs?): if lhs == rhs { return .orderedSame } return lhs < rhs ? .orderedAscending : .orderedDescending } }
最後に
あらためて Swift の強力な表現力を知ることができました。 Optional をサポートする他の言語ではどうなっているか気になってきましたね(?)。
Swift をこれから学ぶ方、普段から書いている人も是非 The Swift Programming Language をもう一度読んでみることをお勧めします。自分も読み直す中で面白い発見があればまた記事にしていきたいと思います。
(実は初ブログなのです)