【Swift】guard文とguard-let文

guard文とは条件を満たさない場合の処理を書く構文で、コードを安全にするために使います。
if-else文でも同様のことはできますが、guard文は条件を満たさない場合のみの処理を書くことができます。
安全にメソッドの処理を途中で終了(return)したいとき、ループ処理の中断(break)したいときなどに使います。

前回は break文 と continue文 について書きました。

今回は guard文 について書いていこうと思います。

guard文

guard文 とは

guard文 とは条件を満たさない場合の処理を書く構文で、コードを安全にするために使います。
if-else文 でも同様のことはできますが、guard文 は条件を満たさない場合のみの処理を書くことができます。
安全にメソッドの処理を途中で終了( return )したいとき、ループ処理を中断( break )したいときなどに使います。

guard文 の書式

 guard 条件 else{
     処理
     return または break
 }

guard文 は基本的に関数内や制御構文内に書きます。
guard キーワードの後に条件を書いて、その後に else キーワードをつけます。
{ } の中に処理を書きますが、処理は条件が false だった場合に実行されます。
関数や制御構文のスコープを抜けるために return または break などが必要です。

guard文 を使った例

let slot = [145, 214, 405, 315, 777, 343]
let stop = slot.randomElement() ?? 123

func jackpot(num: Int){
    guard num == 777 else{
        print("\(num) ハズレ:関数から抜けます。")
        return
    }
    print("\(num) 大当たり!")
}

jackpot(num: stop)

配列slot に 3 桁の整数を要素として複数代入しています。
配列slot の要素の中からランダムに要素を取り出して、定数stop に代入します。

関数jackpot を定義し Int型 の引数を取って、引数の値によって処理を分岐します。
guard文 の条件は 引数num の値が 777 ではなかったらguard文 の処理が実行されて、return ( 関数から抜ける )します。これを 早期リターン と言います。
引数num の値が 777 だったらguard文 の条件から外れるので、return されずに次の print( ) が実行されアンラップした値を出力します。

randomElement( ) メソッドはコレクション型の要素をランダムに返すメソッドです。返ってくる値は Optional型 なのでアンラップして 定数stop に代入しています。

関数jackpot の引数に 定数stop を指定して実行してみます。

let slot = [145, 214, 405, 315, 777, 343]
let stop = slot.randomElement() ?? 123

func jackpot(num: Int){
    guard num == 777 else{
        print("\(num) ハズレ:関数から抜けます。")
        return
    }
    print("\(num) 大当たり!")
}

jackpot(num: stop)

//実行結果
/*
145 ハズレ:関数から抜けます。
*/

ランダムに取り出された要素が 145 でした。
条件が false になるので、guard文 の処理を実行して return されます。
return されている( 関数から抜けている )ので、下の print( ) は実行されません。

guardコードimg

次は 777 だった場合の実行結果を見てみます。

let slot = [145, 214, 405, 315, 777, 343]
let stop = slot.randomElement() ?? 123

func jackpot(num: Int){
    guard num == 777 else{
        print("\(num) ハズレ:関数から抜けます。")
        return
    }
    print("\(num) 大当たり!")
}

jackpot(num: stop)

//実行結果
/*
777 大当たり!
*/

777 だった場合は、guard文 の条件から外れているので return されず下の print( ) が実行されます。

guardコードimg2

guard-let文

guard-let文 とは

guard-let文 とはオプショナルバインディングの 1 つで、Optional型 をアンラップするときに使います。
if-let文 でもアンラップすることができますが、if-let文スコープ内でのみアンラップした定数を使うことができるのに対し、guard-let文スコープの外でも定数を使うことができます。

guard-let文 の書式

guard let 定数名 = Optional型の値 else{
     アンラップに失敗した場合の処理
     return または break
 }

guard-let文guard文 同様、基本的に関数内や制御構文内に書きます。
guard let キーワードの後に定数名を書いて、アンラップしたい Optional型の値 の後に else キーワードを付けます。
{ } の中は、if-let文 と違い、Optional型の値 をアンラップ失敗した場合の処理を書きます。
アンラップ失敗したときの処理なので、スコープを抜けるために return または break などが必要です。

guard-let文 を使った例

let aHalf = ["1", nil, "2", nil, "3", nil]
print(type(of: aHalf)) //Array<Optional<String>>
let stop = Int.random(in: 0 ..< aHalf.count)

func judgement(){
    guard let unwrap = aHalf[stop] else{
        print("nil 関数から抜けます。") //nilだった場合
        return
    }
    print("\(unwrap)") //nilでなければアンラップして出力

}

judgement()

配列aHalfString型 の要素と、nil を 3 つずつ代入しています。
この配列は nil も代入されているので、Optional<String> 型 の配列になります。
random(in:) メソッドを使って、0 ~ 9 の中からランダムに返された値を 定数stop に代入します。
この 定数stop の値をインデックス番号として使います。

関数judgement を定義し、処理で guard-let文 を使って 配列aHalf の要素をアンラップしていきます。
ランダムに選択された aHalf[stop] の値が、アンラップ失敗( 値が nil )であれば、“nil 関数から抜けます。” と出力し return で関数を抜けます。
aHalf[stop] の値がアンラップ成功であれば、guard-let文 の条件から外れるので、return されずに 次の print( ) が実行されて、アンラップされた値を出力します。

if-else文 ではスコープ内に定義した定数はスコープ内でしか使えませんでしたが、guard-let文スコープ外でもアンラップした定数が使う事ができます。

random(in:) は、指定された範囲内でランダムな値を返すメソッドです。
Int.random(in: 0 ..< aHalf.count) とすると、0 から 配列aHalf の要素数 6 ( 6 は含まない ) となるので 0 ~ 5 の中からランダムな整数が返されます。

guardコードimg3

以上 guard文 についてでした。

次回は switch文 について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/

【Swift】Optional型

Optional型( オプショナル型 )とは、nilの可能性があるときに使う型です。
Swiftでは値が無い状態のことを、nilと言います。
例えば、必須項目や非必須項目をユーザーに入力してもらう時など、入力されるか分からない項目がある場合などに Optional型を使います。

前回は タプル について書きました。

今回は Optional型 について書いていこうと思います。

Optional型とは

Optional型( オプショナル型 )とは、nil の可能性があるときに使う型です。
Swift では 値が無い 状態のことを、nil と言います。
逆に言うと、Optional型 で宣言している場合のみ nil を代入する事ができます。
例えば、必須項目 や 非必須項目 をユーザーに入力してもらう時など、入力されるか分からない項目がある場合などに Optional型 を使います。

Optional型の書式

//書式1
型名? 

//書式2
Optional<型名>

型名を書いた後に ? を付けます。( 書式 1 )
または
Optional と書いた後 < > の中に型名を書きます。( 書式 2 )

Optional型 の書き方

//例1
var a:Int?
print(type(of: a)) //Optional<Int>

//例2
var b:Optional<Int>
print(type(of: b)) //Optional<Int>

変数 abOptional型 で宣言しています。
この 2 つの変数の型を見てみると、Optional<Int> となっています。これは Optional Int型 という型で、ただの Int型とは別の型になります。なので原則同じ型でなければ演算できないので、Optional Int型Int型 は演算できません。
Optional型 の値を使いたい時は、アンラップ という処理が必要になります。

Int型 に限らず、他の型でも頭に Optional と付いている型の値を使いたい時はアンラップが必要です。

※ 例 1 は初期値を入れずに宣言するとデフォルトで nil が代入されますが、
例 2 は初期値を入れずに宣言しても nil が代入されません。例 2 はどこかで値を代入しないと使うことができないので注意。

//例1
var a:Int?
print(type(of: a)) //Optional<Int>
print(a) //nil

//例2
var b:Optional<Int> = nil //初期値としてnilを代入
print(type(of: b)) //Optional<Int>
print(b) //nil

Optional型を使った例

必須項目と非必須項目がある場合の例

//必須項目と非必須項目がある場合の例
let name:String //必須項目
var mail:String //必須項目

//Optional型で非必須項目の宣言
var phone:String? //非必須項目
var birthday:String? //非必須項目

//必須項目に値を代入
name = "hoge"
mail = "aaa@123.com"

//アクセス
print("\(name)のアカウント情報") //hogeのアカウント情報
print("アカウント名 \(name)") //アカウント名 hoge
print("メールアドレス \(mail)") //メールアドレス aaa@123.com

//非必須項目にアクセス
print("電話番号 \(phone)") //電話番号 nil
print("誕生日 \(birthday)") //誕生日 nil

必須項目と非必須項目に使う変数・定数を宣言して、必須項目にだけ値を代入し全ての変数・定数にアクセスしている例です。
必須項目には値を代入しているのでこれまで通りアクセスできて値もきちんと反映されています。
次に非必須項目ですが、 値を代入していません。
Optional型 で宣言しているので値の代入がされてなくてもデフォルトで初期値に nil が代入されます。
Optional型nil許す型なので、アクセスしたとき値が無くてもエラーやクラッシュではなく nil が返ってきます。
もしこれを Optional型 ではなく String型 などで宣言していれば、値が無い( 初期値が無い )のにアクセスしてしまいエラーやクラッシュする原因になります。

次は非必須項目に値を入れてアクセスしてみます。

//必須項目と非必須項目がある場合の例
let name:String //必須項目
var mail:String //必須項目

//Optional型で非必須項目の宣言
var phone:String?//非必須項目
var birthday:String? //非必須項目

//必須項目に値を代入
name = "hoge"
mail = "aaa@123.com"

//アクセス
print("\(name)のアカウント情報") //hogeのアカウント情報
print("アカウント名 \(name)") //アカウント名 hoge
print("メールアドレス \(mail)") //メールアドレス aaa@123.com

//非必須項目にアクセス
print("電話番号 \(phone)") //電話番号 nil
print("誕生日 \(birthday)") //誕生日 nil

//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//非必須項目に再度アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

非必須項目にも値を代入して再度アクセスしました。
値を入れずにアクセスした時は nil が代入されていましたが、値を入れてアクセスすると nil ではなく、
phone には Optional(“123-4567-8910”)
birthday には Optional(“20XX/XX/XX”)
が代入されています。
String型 の値を代入したのに、この 2 つの変数は Optional String型 で宣言されているので、頭に Optional が付いた状態になっています。
この状態では print関数 で中の値は確認できますが、実際に値を使うことはできません。
使おうとすると、アンラップする必要があります!とエラーが出ます。

~ ~ ~
//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//非必須項目に再度アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

//Optional String型の変数をString型の変数に入れようとするとエラー
var phoneNumber:String = phone //エラー
optionalコードimg

アンラップ

アンラップとは、ラップ( 包装 )の反対語でラップを外すこと。と表現されている事が多いと思います。
Optional型 はラップ( 包装 )された状態で、このラップを外すことをアンラップと言います。

Optional型の値を 非Optional型 の値と同様に扱いたいときは、アンラップ( unwrap )という処理をしなければ使うことができません。

アンラップする方法

  • ?? 演算子( nil合体演算子 )
  • ! 演算子( 強制アンラップ )
  • if-let文 ( オプショナルバインディング )

この他にも guard-let などいくつかアンラップする方法がありますが、今回は上記の 3 種類を使ってアンラップしていきたいと思います。

?? 演算子 ( nil合体演算子 )

?? 演算子を使ったアンラップの書式

Optional型の値 ?? アンラップ失敗または、nilだった場合に入る値(デフォルト値)

左辺の Optional型の値 がアンラップ可能であれば アンラップした値 を代入し、
アンラップ失敗( 値が nil ) であれば右辺の デフォルト値 を代入する演算子です。

~ ~ ~
//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

//アンラップ
//??演算子(nil合体演算子)
var phoneNumber:String = phone ?? "アンラップ失敗"

//アンラップ後に再度アクセス
print("電話番号 \(phoneNumber)") //電話番号 123-4567-8910
print(type(of: phoneNumber)) //String

Optional型変数 phone がアンラップ可能であれば アンラップした値変数 phoneNumber に代入します。
もしアンラップ失敗や、そもそも値が nil であれば右辺の “アンラップ失敗” という文字列を phoneNumber に代入します。

アンラップ前の phone の値には Optional と頭に付いていましたが、
アンラップ後の値を見るとOptional が取れて普通の String型 にアンラップされています。これで String型 の値として扱うことができるようになりました。
アンラップが成功した場合と失敗した場合の両方の処理を書くので、安全にアンラップする方法だと思います。

! 演算子 ( 強制アンラップ )

! 演算子を使ったアンラップの書式

Optional型の値!

Optional型の値 の後に ! を書くことで強制的にアンラップする事ができる演算子です。

~ ~ ~
//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

//アンラップ
//!演算子(強制アンラップ)
var phoneNumber:String = phone!

//アンラップ後に再度アクセス
print("電話番号 \(phoneNumber)") //電話番号 123-4567-8910
print(type(of: phoneNumber)) //String

Optional型変数 phone の後に ! を書いて強制的にアンラップした値を 変数 phoneNumber に代入します。
アンラップ後のアクセスした結果を見るとアンラップされているのが分かります。

※ ただし ! 演算子( 強制アンラップ )は、 Optional型の値nil だった場合に強制アンラップするとプログラムがクラッシュしてしまいます。
値が nil なのに( アンラップの対象が無いのに )強制アンラップしているためです。
なので nil の可能性がある場合、値が入っているか分からない場合は使えません。
確実に値が入っていると分かっている場合に使います。

if-let文 ( オプショナルバインディング )

if-let文 を使ったアンラップの書式

if let 定数名 = Optional型の値 {
    アンラップに成功したらこの中の処理が実行
} else {
    アンラップ失敗したらこの中の処理が実行される
    else文を書いていない場合、このif-let文はスキップ
}

Optional型の値 がアンラップ可能であれば、アンラップした値を定数に代入し、{ } の中の処理が実行されます。
アンラップ失敗( 値が nil )であれば else文{ } の中の処理が実行されます。

else文 を書かなかった場合、アンラップ失敗( 値が nil )であれば if-let文 はスキップされます。

~ ~ ~
//非必須項目に値を代入
phone = "123-4567-8910"
birthday = "20XX/XX/XX"

//アクセス
print("電話番号 \(phone)") //電話番号 Optional("123-4567-8910")
print("誕生日 \(birthday)") //誕生日 Optional("20XX/XX/XX")

//アンラップ
//if-let文(オプショナルバインディング)
if let phoneNumber = phone {
    print(phoneNumber) //電話番号 123-4567-8910
    print(type(of: phoneNumber)) //String
} else {
    print("アンラップ失敗")
}

Optional型変数 phone がアンラップ可能であれば アンラップした値定数phoneNumber に代入し、{ } の中の処理を実行します。
アンラップ失敗( 値が nil )であれば else文{ } の中の処理が実行されます。
このアンラップ方法も アンラップが成功した場合と失敗した場合の両方の処理を書けるので、nil の値をアンラップしようとしてクラッシュするなどのエラー回避につながります。

エラーやクラッシュを回避するためにも、できるだけ強制アンラップは使わず、?? 演算子や if-let文 でアンラップした方が安全だと思います。

お疲れ様でした。
以上 Optional型 についてでした。

次回は while文 について書いていこうと思います。

実行環境

version
Xcode 14.2 (14C18)
Swift 5.2.4

公式ドキュメント

https://docs.swift.org/swift-book/documentation/the-swift-programming-language/