🫥

`null` 代わりの `Option<T>` という基本的なデータ型

に公開

null 代わりの Option<T> という基本的なデータ型

Rust には Option<T> という型があります。だいたいこういう定義です。

enum Option<T> {
    None,
    Some(T),
}

Rust は暗黙に null を許容する参照型……のようなものはありません。代わりに明示的に Option<T> 型を使用します。値がないことを None 、値があることを Some で示します。 (いきなり余談ですが std::ptr::null があるので null がないはウソになりますね)

Option<T> は標準ライブラリを含めて広く使われている型なので、 Rust をはじめるとすぐに触れることになります。単純な構造ではあるのですが、ぼくの所属するドクターメイト株式会社でのメンバーの経過を見るに、慣れて使いこなすまでには意外と時間がかかっているように思います。

基本的なところから順に押さえるためにも、改めて書いてみます。

個人の意見: 暗黙に null を許容されるのが嫌い

また余談ですが、ぼくは暗黙に null を許容されるのが嫌いです。

「もしかしたら T 型の変数の値が null かもしれない」と不安になったり、「何かの間違いで Optional<T> 型の変数の値が null かもしれない」とおびえたりします。そういうことに不安を感じたり、おびえたりするのが嫌です。ぼくは何が嫌いかは何が好きかと同じくらい自分を語る上で大切だと思っています。

Rust で null に相当する NoneOption<T> 型の値にしか出てきません。安心です。

基本的な使い方

Option<T> は、たとえば次のように構築できます。

let o1: Option<i32> = None;
let o2: Option<i32> = Some(123);

Option<T> は、たとえば次のように分解できます。

let o: Option<i32> = Some(123);
match o {
    None => println!("None!"),
    Some(v) => println!("Some({})", v),
}

match前回の「 if より match 」 でも触れていますね。

今回はあえて if (bool) を使う形にもしてみます。

bool への変換の例

is_none / is_some

単に NoneSome かを判断したいなら is_noneis_some を使えます。

let o1: Option<i32> = None;
assert!(o1.is_none());
let o2: Option<i32> = Some(123);
assert!(o2.is_some());

TypeScript のような Narrowing があるわけではないので、 is_some を呼び出したからといって値を取り出すには他のメソッドの呼び出しが必要です。

let o: Option<i32> = Some(123);
if o.is_some() {
    // unwrap などを呼び出さないと 123 を取り出せない
    assert_eq!(o.unwrap(), 123);
}

is_some_and / is_none_or

中身を考慮しての判定が必要なだけなら unwrap を避けて is_some_and も使えます。

let o: Option<i32> = Some(4);
assert!(o.is_some_and(|n| n % 2 == 0));

類似の is_none_or もあります。

ドクターメイト株式会社での運用では……

ドクターメイト株式会社での運用では「 if より match 」の影響からか、 is_someis_some_and などの使用頻度はそこまで高くないです。必要に応じて使っていきたいところです。

その他の変換 (次回予告)

他にも変換はいろいろあります。分解することなく Option<T> の中身だけを変換したり、さきほどの unwrap のような中身の取り出しもその他の変換です。こういった変換については次回改めて書こうと思います。

参考

GitHubで編集を提案
ドクターメイト

Discussion