`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
に相当する None
は Option<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
単に None
か Some
かを判断したいなら is_none
と is_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_some
や is_some_and
などの使用頻度はそこまで高くないです。必要に応じて使っていきたいところです。
その他の変換 (次回予告)
他にも変換はいろいろあります。分解することなく Option<T>
の中身だけを変換したり、さきほどの unwrap
のような中身の取り出しもその他の変換です。こういった変換については次回改めて書こうと思います。
Discussion