S-JIS[2024-09-17/2024-10-03] 変更履歴

Rust Option列挙型

Ruststd::option::Optionのメモ。


概要

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

Optionの実装のされ方

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のメソッド

Option<T>のメソッド(抜粋)。[2024-10-03]

メソッド 説明
is_some(&self) -> bool Someかどうか。 let some = Some(123);
let none: Option<i32> = None;
assert_eq!(true, some.is_some());
assert_eq!(false, none.is_some());
is_some_and(self, f: impl FnOnce(T) -> bool) -> bool Someのときはfを実行してその結果を返す。
Noneのときはfalseを返す。
let some = Some(123);
let s100 = Some(100);
let none: Option<i32> = None;
assert_eq!(true, some.is_some_and(|n| n > 100));
assert_eq!(false, s100.is_some_and(|n| n > 100));
assert_eq!(false, none.is_some_and(|n| n > 100));
is_none(&self) -> bool Noneかどうか。 let some = Some(123);
let none: Option<i32> = None;
assert_eq!(false, some.is_none());
assert_eq!(true, none.is_none());
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);
let n: i32 = opt.expect("error");
assert_eq!(123, n);
unwrap(self) -> T Someのときは値(と所有権)を返す。
Noneのときはパニックを起こす。
?演算子
let opt = Some(123);
let n: i32 = opt.unwrap();
assert_eq!(123, n);
unwrap_or(self, default: T) -> T Someのときは値(と所有権)を返す。
Noneのときは引数defaultの値を返す。
let some = Some(123);
let none: Option<i32> = None;
assert_eq!(123, some.unwrap_or(100));
assert_eq!(100, none.unwrap_or(100));
unwrap_or_default(self) -> T
T: Default
Someのときは値(と所有権)を返す。
NoneのときはTのデフォルト値を返す。
TがDefaultトレイトを実装しているとき(デフォルト値を返せるとき)だけ使用可能。
let some = Some(123);
let none: Option<i32> = None;
assert_eq!(123, some.unwrap_or_default());
assert_eq!(0, none.unwrap_or_default());
unwrap_or_else<F>(self, f: F) -> T
F: FnOnce() -> T
   
unwrap_unchecked(self) -> T    
as_ref(&self) -> Option<&T> Option<T>に対してOption<&T>を返す。 let some = Some(123);
let r: Option<&i32> = some.as_ref();
as_mut(&mut self) -> Option<&mut T> Option<T>に対してOption<&mut T>を返す。 let mut some = Some(123);
let r: Option<&mut i32> = some.as_mut();
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>
F: FnOnce(T) -> U
Option<T>をOption<U>に変換する。 let opt = Some(123);
let s = opt.map(|n| format!("n={}", n));
map_or<U, F>(self, default: U, f: F) -> U
F: FnOnce(T) -> U
   
map_or_else<U, D, F>(self, default: D, f: F) -> U
D: FnOnce() -> U
F: FnOnce(T) -> U
   
inspect<F>(self, f: F) -> Self
F: FnOnce(&T)
   
ok_or<E>(self, err: E) -> Result<T, E> Option<T>をResult<T, E>に変換する。 let opt = Some(123);
let r: Result<i32, &str> = opt.ok_or("エラー");
ok_or_else<E, F>(self, err: F) -> Result<T, E>
F: FnOnce() -> 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
P: FnOnce(&T) -> bool
Someのとき、Pを満たせば自分自身を返す。満たせなかったらNoneを返す。 let some = Some(123);
let s100 = Some(100);
let none: Option<i32> = None;
assert_eq!(Some(123), some.filter(|n| *n > 100));
assert_eq!(None, s100.filter(|n| *n > 100));
assert_eq!(None, none.filter(|n| *n > 100));
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
T: Default
   
get_or_insert_with<F>(&mut self, f: F) -> &mut T
F: FnOnce() -> T
   
take(&mut self) -> Option<T> 値の所有権を奪う。
元のOptionはNoneに変わる。
struct MyStruct {
  opt: Option<i32>,
}

let mut s = MyStruct { opt: Some(123) };
let t = s.opt.take();
assert_eq!(Some(123), t);
assert_eq!(None, s.opt);
take_if<P>(&mut self, predicate: P) -> Option<T>
P: FnOnce(&mut T) -> bool
   
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>
F: FnOnce(T, U) -> R
   

その他に、Option<T>のTが特定の条件の場合だけ使用できるメソッドもある。


TがCloneトレイトを実装している場合

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<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<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トレイトを実装している場合

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);

Optionと ?演算子

Resultで「?演算子」が使えるのと同様に、Optionでも?演算子を使うことが出来る。

Optionの?演算子は、Someのときはそれが保持している値を返し、NoneのときはNoneをreturnする(関数からNoneを返す)。

fn func1() -> Option<i32> {
    let n = func2()?;
    Some(n + 1)
}
fn func2() -> Option<i32> {
    None
}

構造体のOptionフィールドを取得する例

構造体のOptionフィールドを取得する例。[2024-10-03]


Option<T>のTが参照でない場合

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")
    }
}

Option<T>のTが不変参照の場合

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")
    }
}

Option<T>のTが可変参照の場合

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")
    }
}

Option<T>のTがCloneを実装している場合

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")
    }
}

stdへ戻る / Rustへ戻る / 技術メモへ戻る
メールの送信先:ひしだま