読者です 読者をやめる 読者になる 読者になる

男女比はカレーと福神漬けと同じくらい

マサカリよろしくお願いします。

RubyとSwiftにおける感嘆符/疑問符の扱いの違い

RubyとSwiftでは「!」と「?」が多用されますが使い方が違います。
Swiftでの使い方に中々慣れなかったので自分なりに対比させてまとめてみました。

元々Rubyを書いていてこれからSwiftを書き始める人やその逆の人の参考になればと思います。

 

最も違うところ

Rubyメソッド名に「!」,「?」を使う

Swiftは型名, 変数名に「!」,「?」を使う

 

Rubyはメソッド名に「!」,「?」を使う

Rubyではメソッドの処理内容をメソッド名から類推しやすくするために「!」「?」を使います。

Rubyリファレンスには以下のように記載されています。

「!」はメソッド名の一部です。慣用的に、 同名の(! の無い)メソッドに比べてより破壊的な作用をもつメソッド(例: tr と tr!)で使われます。
 
「?」はメソッド名の一部分です。 慣用的に、真偽値を返すタイプのメソッドを示すために使われます。

 

具体例を示します。

# 破壊的なメソッド
a = "string"
b = a
a.upcase!
p a   # => "STRING"
p b   # => “STRING"

# 非破壊的なメソッド
a = "string"
b = a
a = a.upcase
p a   # => "STRING"
p b   # => “string”

# 真偽を返す
“”.empty? #=> true

rubyのメソッド名で「!」「?」はアルファベットと同じ扱いをします。
なのでupcaseupcase!のような「!」「?」がついているメソッドとついていないメソッドは似た処理を行う別のメソッドになります。 同様の理由で非破壊メソッドのhogeメソッドしか定義してないときにhoge!を呼び出してもhogeの処理に似た破壊的メソッドが実行されるわけではありません(当たり前ですが)。

"メソッドの処理内容をメソッド名から類推しやすくする”ための機能なのでmustではありません。shouldです。 「!」がついていないけど破壊的な変更を行うメソッドや「?」がついていないけど真偽値を返すメソッドを定義してもエラーを吐かれることはありません。

 

Swiftは型名,変数名に「!」,「?」を使う

swiftでは変数の値がnilである状態を許容するかどうかを明確に区別するために「!」「?」を使います。
nilを許容するかどうかを明確に判断することによりnil関連のエラーに出会う可能性を極力減らすことができます。

具体例を示します。

# nilである状態を許容しない
var x = 3
x = nil     # error: nil cannot be assigned to type ‘Int’
print(x)    # 上の行でエラーしているので実行できない

var y: Int = 3
y = nil     # error: nil cannot be assigned to type ‘Int’
print(y)    # 上の行でエラーしているので実行できない


# nilである状態を許容する
var z: Int? = 3
z = nil
print(z)    # => nil

# -----------------

#Int型の変数にInt?型の値を代入する(unwrapしない)
var a: Int = 5
var b:Int? = 10
var c:Int? = nil
a = b       # error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
print(a)    # 上の行でエラーしているので実行できない
a = c       # error: value of optional type 'Int?' not unwrapped; did you mean to use '!' or '?'?
print(a)    # 上の行でエラーしているので実行できない

#Int型の変数にInt?型の値を代入する(unwrapする)
var d:Int = 5
var e:Int? = 10
var f:Int? = nil
d = e!
print(d)    # => 10
d = f!      # fatal error: unexpectedly found nil while unwrapping an Optional value
print(d)    # => 10 (d = e!の結果が残っている)

Intの変数にInt?の変数を直接代入することはできません。
それはIntInt?が別の型でありInt?の変数の値がnilの可能性があるためです。
変数名の後ろに「!」をつけることにより値がnilでないことを明示する(アンラップする)ことができます。 そしてアンラップした変数は「?」のついていない型に代入できます。

Swiftでの「!」「?」はmustです。
正しくラップ/アンラップしなければコンパイルエラーを吐きます。    

Swiftの「!」「?」はまだ理解しきれていない部分があるので代表的なところだけを紹介しました。
この記事がかなり詳しいです。
[Swift] Optional 型についてのまとめ Ver2 - Qiita