SwiftのOptionalをC++のnullptrやstd::optionalと比べて整理する

Swiftの String? は、C++のポインタや std::optional を知っていると理解しやすいです。

ただし、nullptr とは少し違います。

公式では、optionalは値があるか、まったく値がないかを扱うものとして説明されています。

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

Optionalは値があるかないか

var name: String? = "Makonote"
name = nil

String? は「Stringかnil」です。

String とは別の型です。

let a: String = "A"
let b: String? = "B"

b はそのまま String として使えません。

C++のstd::optionalに近い

C++で近いのは std::optional<T> です。

std::optional<std::string> name = "Makonote";
name = std::nullopt;

Swiftの String? も、「あるかもしれない値」です。

ポインタのnullというより、値の有無を型で表していると見る方が自然です。

if letで取り出す

Optionalの中身を安全に取り出すには if let を使います。

let name: String? = "Makonote"

if let name {
    print(name)
}

中の nameString です。

C++ならこういうチェックに近いです。

if (name) {
    std::cout << *name;
}

guard letで早く抜ける

関数の先頭で値がない場合に抜けたいなら guard let が便利です。

func greet(_ name: String?) {
    guard let name else {
        return
    }

    print("Hello, \(name)")
}

Swiftでは、この形がかなりよく出ます。

ネストが浅くなるので、if let より読みやすいことがあります。

!はなるべく避ける

! を使うと強制的に取り出せます。

let name: String? = nil
print(name!)

これは実行時に落ちます。

C++でいう、nullかもしれないポインタをそのままdereferenceする感覚に近いです。

std::string* name = nullptr;
std::cout << *name;

どうしても「ここでは絶対にnilではない」と言える場所だけに絞った方がいいです。

まとめ

SwiftのOptionalは、C++の nullptr より std::optional に近いです。

  • T? は「Tがあるかもしれない」
  • TT? は別の型
  • if let で安全に取り出す
  • 早く抜けるなら guard let
  • ! はクラッシュの可能性がある

Optionalを雑に外すより、nilの場合にどうするかを先に決めて書く方がSwiftでは読みやすいです。

関連記事

参考