|
Option列挙型は、値が存在しない場合があることを表す列挙型。
Option列挙型はstd::preludeに含まれているので、useしなくても使える。
Optionインスタンスを作るには、値がある場合はSomeを使い、値が無い場合はNoneを使う。
// 値を保持 let opt = Some(123); println!("{:?} {}", opt, opt.is_some()); // →true // 値が無い let opt: Option<i32> = None; println!("{:?} {}", opt, opt.is_some()); // →false
RustのOptionは列挙型で実装されている。
JavaのOptionalはひとつのクラスのみで定義され、ScalaのOptionは抽象クラスでSomeとNoneはそれを継承した別々のクラスになっている。
各言語がそれぞれの機能を駆使して実装しているのが面白い。
※以下のコードは、元のソースから分かりやすいように切り出したり変更したりしたもの
項目 | Rust(option.rs) | Java相当 | Scala相当(Option.scala) |
---|---|---|---|
定義 |
pub enum Option<T> { None, Some(T), } |
public final class Optional<T> { private static final Optional<?> EMPTY = new Optional<>(null); public static<T> Optional<T> empty() { return (Optional<T>) EMPTY; } public static <T> Optional<T> of(T value) { return new Optional<>(Objects.requireNonNull(value)); } private final T value; private Optional(T value) { // コンストラクター this.value = value; } 〜 } |
sealed abstract class Option[+A] { 〜 } final case class Some[+A](value: A) extends Option[A] { 〜 } case object None extends Option[Nothing] { 〜 } |
Rustの列挙型は、列挙子ごとに値を保持したりしなかったり出来る。 Some(T)はタプル構造体のような定義方法で、 Noneは値を持たない定義方法。 |
JavaはOptinalクラスのみを定義し、SomeやNoneに当たるものを取得するメソッドがある。 Noneに当たるものの実体として定数EMPTYを用意している。 また、実際の値を保持するvalueというフィールドがある。(Noneのときはvalueはnull) |
Scalaでは親クラスであるOptionを定義し、SomeもNoneもOptionを継承した別のクラスになっている。 「 case class Some(value: A) 」ではvalueというフィールドを定義している。「 case object None 」のobjectは、親クラスを継承した新しいクラスを定義しつつ、シングルトンインスタンス(アプリケーション内でただ一つのインスタンス)を作る構文。 |
|
Someの生成 |
let s = Some(123); |
var s = Optional.of(123); |
val s = Some(123) |
Some列挙子に値を指定してインスタンスを作る。 値が参照でない場合、値の所有権を奪う。 |
Javaではofメソッドに値を渡してSome相当のインスタンスを作る。 | ScalaのSome(123) はSome.apply(123) というメソッド呼び出し。(Scalaではapplyというメソッド名のときは「 .apply 」を省略して呼び出すことが出来る。そして、case classではapplyというメソッドが自動的に生成される) |
|
Noneの取得 |
let s1: Option<i32> = None; let s2 = None as Option<i32>; |
Optional<Integer> s1 = Optional.empty(); var s2 = Optional.<Integer>empty(); |
val s1: Option[Int] = None val s2 = None: Option[Int] |
None列挙子を使用する。 | JavaではemptyメソッドでNone相当のインスタンスを取得する。 | ||
判定の定義 |
pub const fn is_some(&self) -> bool { matches!(*self, Some(_)) } pub const fn is_none(&self) -> bool { !self.is_some() } |
public boolean isPresent() { return this.value != null; } public boolean isEmpty() { return this.value == null; } |
final def isDefined: Boolean = !isEmpty final def isEmpty: Boolean = this eq None |
matches!はマクロで、is_someでは以下のように展開される。pub const fn is_some(&self) -> bool { match *self { Some(_) => true, _ => false } } |
Javaではvalueフィールドで実際の値を保持しており、None相当の場合はvalueはnull。 | ScalaではNoneインスタンスと等しいかどうかで判定している。 | |
値取得の定義 |
pub const fn unwrap(self) -> T { match self { Some(val) => val, None => unwrap_failed(), } } |
public T get() { if (this.value == null) { throw new NoSuchElementException("No value present"); } return this.value; } |
// class Some def get: A = value // object None def get: Nothing = throw new NoSuchElementException("None.get") |
unwrapの第1引数はselfである。(&が付いていない) つまり、保持している値が参照でない場合は、呼び出し元から所有権を奪っている。(unwarpを呼んだ後は、呼び出し元のOptionはそれ以降使用不可能になる) |
ScalaのSomeとNoneは別クラスなので、getメソッドはそれぞれのクラス専用の実装になっている。 |
Option<T>のメソッド(抜粋)。[2024-10-03]
メソッド | 説明 | 例 |
---|---|---|
is_some(&self) ->
bool |
Someかどうか。 | let some = Some(123); |
is_some_and(self,
f: impl FnOnce(T) -> bool) -> bool |
Someのときはfを実行してその結果を返す。 Noneのときはfalseを返す。 |
let some = Some(123); |
is_none(&self) ->
bool |
Noneかどうか。 | let some = Some(123); |
is_none_or(self,
f: impl FnOnce(T) -> bool) -> bool |
||
unwrap系 |
Tが可変参照(&mut)のときは
、unrwapして関数から返すことは出来ない。(コンパイルエラーになる) →フィールドに保持したOption<可変参照>を取得する例 |
|
expect(self, msg:
&str) -> T |
Someのときは値(と所有権)を返す。 Noneのときはメッセージ付きのパニックを起こす。 |
let opt = Some(123); |
unwrap(self) -> T |
Someのときは値(と所有権)を返す。 Noneのときはパニックを起こす。 →?演算子 |
let opt = Some(123); |
unwrap_or(self,
default: T) -> T |
Someのときは値(と所有権)を返す。 Noneのときは引数defaultの値を返す。 |
let some = Some(123); |
unwrap_or_default(self)
-> T |
Someのときは値(と所有権)を返す。 NoneのときはTのデフォルト値を返す。 TがDefaultトレイトを実装しているとき(デフォルト値を返せるとき)だけ使用可能。 |
let some = Some(123); |
unwrap_or_else<F>(self,
f: F) -> T |
||
unwrap_unchecked(self)
-> T |
||
as_ref(&self) ->
Option<&T> |
Option<T>に対してOption<&T>を返す。 | let some = Some(123); |
as_mut(&mut self) ->
Option<&mut T> |
Option<T>に対してOption<&mut T>を返す。 | let mut some = Some(123); |
as_pin_ref(self:
Pin<&Self>) -> Option<Pin<&T>> |
||
as_pin_mut(self:
Pin<&mut Self>) -> Option<Pin<&mut T>> |
||
as_slice(&self) ->
&[T] |
||
as_mut_slice(&mut
self) -> &mut [T] |
||
map<U, F>(self, f: F) ->
Option<U> |
Option<T>をOption<U>に変換する。 | let opt = Some(123); |
map_or<U, F>(self,
default: U, f: F) -> U |
||
map_or_else<U,
D, F>(self, default: D, f: F) -> U |
||
inspect<F>(self, f:
F) -> Self |
||
ok_or<E>(self, err: E)
-> Result<T, E> |
Option<T>をResult<T, E>に変換する。 | let opt = Some(123); |
ok_or_else<E,
F>(self, err: F) -> Result<T, E> |
||
as_deref(&self) ->
Option<&T::Target> |
Tが参照のとき、Option<不変参照>を返す。 | →as_derefの使用例 |
as_deref_mut(&mut
self) -> Option<&mut T::Target> |
Tが可変参照のとき、Option<可変参照>を返す。 | →as_deref_mutの使用例 |
iter(&self) -> Iter<'_,
T> |
||
iter_mut(&mut self)
-> IterMut<'_, T> |
||
filter<P>(self,
predicate: P) -> Self |
Someのとき、Pを満たせば自分自身を返す。満たせなかったらNoneを返す。 | let some = Some(123); |
and<U>(self, optb:
Option<U>) -> Option<U> |
Someをtrue、Noneをfalseに見立てた論理積。 | |
and_then<U,
F>(self, f: F) -> Option<U> |
||
or(self, optb: Option<T>)
-> Option<T> |
Someをtrue、Noneをfalseに見立てた論理和。 | |
or_else<F>(self, f:
F) -> Option<T> |
||
xor(self, optb:
Option<T>) -> Option<T> |
Someをtrue、Noneをfalseに見立てた排他的論理和。 | |
insert(&mut self,
value: T) -> &mut T |
||
get_or_insert(&mut
self, value: T) -> &mut T |
||
get_or_insert_default(&mut self) -> &mut T |
||
get_or_insert_with<F>(&mut self, f: F) -> &mut T |
||
take(&mut self) ->
Option<T> |
値の所有権を奪う。 元のOptionはNoneに変わる。 |
struct MyStruct { |
take_if<P>(&mut
self, predicate: P) -> Option<T> |
||
replace(&mut self,
value: T) -> Option<T> |
||
zip<U>(self, other:
Option<U>) -> Option<(T, U)> |
||
zip_with<U, F,
R>(self, other: Option<U>, f: F) -> Option<R> |
その他に、Option<T>のTが特定の条件の場合だけ使用できるメソッドもある。
TがCloneトレイトを実装していると、Optionでcloneメソッドが使用できる。
clone(&self) -> Self
「Some(t)
」に対して「Some(tのクローン)
」が欲しい時に便利。
use std::rc::Rc; // RcはCloneを実装している struct MyStruct; let rc = Rc::new(MyStruct {}); let opt: Option<Rc<MyStruct>> = Some(rc); let clo: Option<Rc<MyStruct>> = opt.clone();
クローン(複製)したので、cloはoptとは別の所有権になる。
Option<Result<T, E>>だと、Optionでtransposeメソッドが使用でき、Result<Option<T>, E>に変換できる。
transpose(self) -> Result<Option<T>, E>
let opt: Option<Result<i32, String>> = Some(Ok(123)); let r: Result<Option<i32>, String> = opt.transpose();
Option<Option<T>>だと、Optionでflattenメソッドが使用できる。
flatten(self) -> Option<T>
let opt = Some(Some(123)); let flat = opt.flatten(); assert_eq!(Some(123), flat);
Tが演算子用のPartialOrdやPartialEqトレイト等を実装していると、Optionのまま大小比較や等値比較が出来る。
この場合、Noneが最小の値として扱われる。
let some = Some(123); let s100 = Some(100); let none: Option<i32> = None; assert_eq!(true, s100 < some); assert_eq!(true, none < s100);
Resultで「?演算子」が使えるのと同様に、Optionでも?演算子を使うことが出来る。
Optionの?演算子は、Someのときはそれが保持している値を返し、NoneのときはNoneをreturnする(関数からNoneを返す)。
fn func1() -> Option<i32> { let n = func2()?; Some(n + 1) }
fn func2() -> Option<i32> { None }
構造体のOptionフィールドを取得する例。[2024-10-03]
struct MyCommon {} struct MyOpt { field: Option<MyCommon> } let mut opt = MyOpt { field: None }; opt.field = Some(MyCommon {});
フィールドの型 | 取得する型 | 例 | 備考 |
---|---|---|---|
Option<MyCommon> |
MyCommon |
let r = opt.field.unwrap(); |
unwrapを使うと所有権を奪うので、それ以降opt.fieldは使用できない。 |
Option<MyCommon> |
Option<MyCommon> |
let r = opt.field; |
所有権が変数に移動するので、それ以降opt.fieldは使用できない。 |
let r = opt.field.take(); |
takeを使うと所有権を獲得する。 opt.fieldはNoneに変わり、opt.fieldはNoneの所有者になる。 |
||
Option<MyCommon> |
Option<&MyCommon> |
let r = opt.field.as_ref(); |
Option<不変参照>を取得する。 |
Option<MyCommon> |
Option<&mut MyCommon> |
let r = opt.field.as_mut(); |
Option<可変参照>を取得する。 |
Option<MyCommon> |
&MyCommon |
let r = opt.field.as_ref().unwrap(); |
不変参照を取得する。 |
Option<MyCommon> |
&mut MyCommon |
let r = opt.field.as_mut().unwrap(); |
可変参照を取得する。 |
impl MyOpt { pub fn field(&self) -> Result<&MyCommon, &str> { self.field.as_ref().ok_or("field is None") } pub fn field_mut(&mut self) -> Result<&mut MyCommon, &str> { self.field.as_mut().ok_or("field is None") } }
struct MyOpt<'a> { field: Option<&'a MyCommon>, } let mut opt = MyOpt { field: None }; opt.field = Some(&MyCommon {});
フィールドの型 | 取得する型 | 例 | 備考 |
---|---|---|---|
Option<&MyCommon> |
Option<&MyCommon> |
let r = opt.field; |
Option<不変参照>を取得する。 不変参照では参照をコピーするので、フィールドの所有権を奪わない. |
let r = opt.field.as_deref(); |
Option<不変参照>を取得する。 | ||
Option<&MyCommon> |
&MyCommon |
let r = opt.field.unwrap(); |
不変参照を取得する。 |
let r = opt.field.as_deref().unwrap(); |
impl<'a> MyOpt<'a> { pub fn field(&self) -> Result<&MyCommon, &str> { self.field.ok_or("field is None") } }
struct MyOpt<'a> { field: Option<&'a mut MyCommon>, } let mut opt = MyOpt { field: None }; let mut common = MyCommon {}; opt.field = Some(&mut common); // 可変参照の場合、直接「&mut MyCommon{}」とは書けない
フィールドの型 | 取得する型 | 例 | 備考 |
---|---|---|---|
Option<&mut MyCommon> |
Option<&MyCommon> |
let r = opt.field.as_deref(); |
Option<不変参照>を取得する。 |
Option<&mut MyCommon> |
Option<&mut MyCommon> |
let r = opt.field.as_deref_mut(); |
Option<可変参照>を取得する。 |
let r = opt.field; |
Option<可変参照>を取得できるが、可変参照ではフィールドの所有権が変数に移動する。 | ||
Option<&mut MyCommon> |
&MyCommon |
let r = opt.field.as_deref().unwrap(); |
不変参照を取得する。 |
Option<&mut MyCommon> |
&mut MyCommon |
let r = opt.field.as_deref_mut().unwrap(); |
可変参照を取得する。 |
impl<'a> MyOpt<'a> { pub fn field(&self) -> Result<&MyCommon, &str> { self.field.as_deref().ok_or("field is None") } pub fn field_mut(&mut self) -> Result<&mut MyCommon, &str> { self.field.as_deref_mut().ok_or("field is None") } }
Cloneトレイトを実装しているRcの例。
use std::rc::Rc; struct MyOpt { field: Option<Rc<MyCommon>>, } let mut opt = MyOpt { field: None }; opt.field = Some(Rc::new(MyCommon {}));
フィールドの型 | 取得する型 | 例 | 備考 |
---|---|---|---|
Option<Rc<MyCommon>> |
Option<Rc<MyCommon>> |
let r = opt.field.clone(); |
クローン(複製)された新しいインスタンスのOptionを取得する。 フィールドの所有権は元のまま変わらない。 |
impl MyOpt { pub fn field(&self) -> Result<Rc<MyCommon>, &str> { self.field.clone().ok_or("field is None") } }