Swift の inout 引数について改めて調べ直したので、まとめておこうと思います。
環境
- Swift 5.5
inout 引数とは
関数内での引数への再代入を関数外へ反映させる
ことができるそうです。(『[改訂新版]Swift 実践入門』より)
少し言い換えると、引数として受け取った値を変更し、関数外へその値を伝えたい場合に inout 引数を使用すると理解しています。
では、inout ではない普通の引数と比較しながら理解してみようと思います。
まず普通の引数を関数内部で変更してみます。
// 普通の引数の場合
func greetUsual(user: String) {
if user.isEmpty {
user = "Anonymous" // Cannot assign to value: 'user' is a 'let' constant のエラー発生
}
print("Hello, \(user)")
}
もうコードに書いていますが、結論できません。
Cannot assign to value: 'user' is a 'let' constant
というコンパイルエラーが発生します。
では次に、inout 引数で試みます。
関数に関しては普通の引数との違いは、inout
をつけることです。
// inout 引数の場合
func greet(user: inout String) {
if user.isEmpty {
user = "Anonymous" // 引数を変更することができる。エラーは発生しない。
}
print("Hello, \(user)")
}
本当に外部に変更した値を伝えることができているのか、以下のコードで確認してみます。
user1
の値がAnonymous
に変わっていれば、成功です。
また inout 引数をもつ関数を呼び出す時は、以下2点に注意します。
-
&
(アンパサンド)をつけること - 引数には変数を渡すこと(定数ではダメ)
var user1: String = "" // 変数を定義
print("user1(変更前):\(user1)")
greet(user: &user1) // `&`(アンパサンド)をつける
print("user1(変更後): \(user1)")
実行結果はこうなりました。
user1(変更前):
Hello, Anonymous
user1(変更後): Anonymous
成功です!
見事関数の外にあるuser1
という変数に、関数内での引数への再代入を反映させることができました。
greet
関数を実行する前と後で、user1
の値が変化していることが証拠です。
注意点(※コンパイルエラーレベル)
いくつか注意点があったので、記載しておこうと思います。どれもコンパイルエラーで指摘される内容にはなります。
inout 引数には変数を指定する必要がある
inout 引数は何度も書いているように、関数内での引数への再代入を関数外へ反映するという機能なので、定数を指定することはできません。変数を指定する必要があります。
もしも以下のように定数を指定した場合、
Cannot pass immutable value as inout argument: 'user1' is a 'let' constant
のエラーが発生します。
let user1: String = ""
greet(user: &user1) // Cannot pass immutable value as inout argument: 'user1' is a 'let' constant のエラーが発生
inout 引数にはデフォルト値を指定することができない
以下のようにuser
にユーザ1
というデフォルト値を持つように試してみますと、
Cannot provide default value to inout parameter 'user'
のエラーが発生します。
func greet(user: inout String = "ユーザ1") { // Cannot provide default value to inout parameter 'user' のエラーが発生
if user.isEmpty {
user = "Anonymous"
}
print("Hello, \(user)")
}
inout のパラメータには可変長引数を設定することができない
以下のようにuser
を可変長引数にしてみますと、
'inout' must not be used on variadic parameters
のエラーが発生します。
func greet(user: inout String...) { // 'inout' must not be used on variadic parameters のエラーが発生
// do something
}
おわりに
説明しておいてなんですが、いくつかの記事では
関数の処理の中で意図せず値が書き換えられてしまうので、処理を追いづらく、不具合の元になってしまうため、使用はあまり推奨していないという記事を確認しました。
inout を使って書き換えるよりも、関数から戻り値として返す方がいいということかなと理解しています。
今回はそれを念頭に置きつつも、仕事でこの inout 引数を見かけてなんじゃこりゃとなったので調べてみました。
間違いその他コメントあれば、ご指摘いただけると幸いです
参考
- (書籍)『[改訂新版]Swift 実践入門』