0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rust のOption, ResultそしてSome, None, Ok, Err, expect, unwrap, ?演算子,... なにこれ

Posted at

概要

Rustを学んでいて最初に??と思ったのがよく出てくるOptionとResultとそれぞれのメソッドです。
なので、自分なりに使い方を調べて簡単にまとめたものです。

ドキュメント

Option

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

Result

Result定義
pub enum Result<T, E> {
   Ok(T),
   Err(E),
}

実行例

OptionResultはよく似ているので両方を併記します。

データの生成

let o1: Option<i32> = Some(5);
println!("o1 value: {:?}", o1);
let o2: Option<i32> = None;
println!("o2 value: {:?}", o2);

let r1: Result<i32, String> = Ok(12);
println!("r1 value: {:?}", r1);
let r2: Result<i32, String> = Err("Error occurred".into());
println!("r2 value: {:?}", r2);
  • Option<T>の型Tがi32のでSome内の値はi32の型でなければならない
  • Option<T>のもう一つの値はNoneである
  • Result<T, E>の型Tが i32 なのでOk内の値は i32 の型でなければならない
    • 良く見るのは型Tが () のときで、このときはOk(())となる
  • Result<T, E>の型Eが String なのでErr内の値は String の型でなければならない
実行結果
o1 value: Some(5)
o2 value: None
r1 value: Ok(12)
r2 value: Err("Error occurred")

unwrap, expect

これはSomeOk内の値を取り出すが、NoneErrのときはpanicになる

let o1: Option<i32> = Some(5);
let o1_val: i32 = o1.unwrap();
println!("o1 value: {}", o1_val);
let r1: Result<i32, String> = Ok(12);
let r1_val: i32 = r1.unwrap();
println!("r1 value: {}", r1_val);

let o2: Option<i32> = Some(7);
let o2_val: i32 = o2.expect("o2 value is missing");
println!("o2 value: {}", o2_val);
let r2: Result<i32, String> = Ok(18);
let r2_val: i32 = r2.expect("r2 value is missing");
println!("r2 value: {}", r2_val);
実行結果
o1 value: 5
r1 value: 12
o2 value: 7
r2 value: 18

値がちゃんと取り出せています。
次はpanicになる例です。

Option unwrap
let o1: Option<i32> = None;
let o1_val: i32 = o1.unwrap();
println!("o1 value: {}", o1_val);
実行結果(panic)
thread 'main' panicked at src/main.rs:34:26:
called `Option::unwrap()` on a `None` value

println!は実行されず2行目でpanicになっています。

Option expect
let o1: Option<i32> = None;
let o1_val: i32 = o1.expect("o1 value is missing");
println!("o1 value: {}", o1_val);
実行結果(panic)
thread 'main' panicked at src/main.rs:48:26:
o1 value is missing
Result unwrap
let r1: Result<i32, String> = Err("Error occurred".into());
let r1_val: i32 = r1.unwrap();
println!("r1 value: {}", r1_val);
実行結果(panic)
thread 'main' panicked at src/main.rs:40:26:
called `Result::unwrap()` on an `Err` value: "Error occurred"
Result expect
let r1: Result<i32, String> = Err("Error occurred".into());
let r1_val: i32 = r1.expect("r1 value is missing");
println!("r1 value: {}", r1_val);
実行結果(panic)
thread 'main' panicked at src/main.rs:61:26:
r1 value is missing: "Error occurred"

次にexpectの処理をmatchを使って表すと次のようになっている。

let o1: Option<i32> = Some(5);
//let o1_val: i32 = o1.expect("o1 value is missing");
let o1_val: i32 = match o1 {
    Some(val) => val,
    None => panic!("o1 value is missing"),
}; 
println!("o1 value: {}", o1_val);

let r1: Result<i32, String> = Err("Error occurred".into());
//let r1_val: i32 = r1.expect("r1 value is missing");
let r1_val: i32 = match r1 {
    Ok(val) => val,
    Err(err) => panic!("r1 value is missing: \"{}\"", err),
};
println!("r1 value: {}", r1_val);
実行結果
o1 value: 5

thread 'main' panicked at src/main.rs:77:21:
r1 value is missing: "Error occurred"

OptionResultenumなのでmatch文を使えばほとんどのことが出来ますが、それを簡単なメソッドでどう表現するだけだと思います。

?演算子

main.rs
fn main() -> Result<(), String> {
    println!("enter main");
    let rs: u32 = sub_func()?;
    println!("sub_func returned: {}", rs);
    Ok(())
}

fn sub_func() -> Result<u32, String> {
    println!("enter sub_func");
    Ok(10)
}
実行結果
enter main
enter sub_func
sub_func returned: 10
sub_func done

上記の例では戻り値がOkだったので?unwrapのように値を取り出しています。だからといってどこでも?演算子が使えるわけでもありません。?演算子が使えるのは、それを使う関数の戻り値がResultであることです。この例ではmainの戻り値がResultになっているから使えます。さらにErr(E)の型Eが同じでなければなりません。この例ではStringで同じになっています。

次はsub_funcErrにする。

main.rs
fn main() -> Result<(), String> {
    println!("enter main");
    let rs: u32 = sub_func()?;
    println!("sub_func returned: {}", rs);
    Ok(())
}

fn sub_func() -> Result<u32, String> {
    println!("enter sub_func");
    Err("sub_func error".into())
}
実行結果
enter main
enter sub_func
Error: "sub_func error"

Errが返ってくると?演算子は関数内の以降の処理を飛ばしてreturnさせる。さらにpanicにはなっていない。
matchを使って書き直すと次のようになります。

let rs = match sub_func(){
    Ok(val) => val,
    Err(err) => return Err(err),
};

matchを使った例でもわかるようにErr(E)の型Eが一致していないと使えないのは前述したと通りです。例では、型Eがmain,sub_func共にStringなので?演算子が使えた。

その他のメソッド

if let

let o1: Option<i32> = Some(5);
if let Some(val) = o1 {
    println!("o1 value: {}", val);
} else {
    println!("o1 value is missing");
}
let r1: Result<i32, String> = Err("Error occurred".into());
if let Ok(val) = r1 {
    println!("r1 value: {}", val);
} else {
    println!("r1 value is missing: \"{}\"", r1.unwrap_err());
}
実行結果
o1 value: 5
r1 value is missing: "Error occurred"

unwrap_err()Errの内容を取得できます。

is_some(), is_ok()

let o1: Option<i32> = Some(5);
if o1.is_some(){
    println!("o1 value: {}", o1.unwrap());
} else {
    println!("o1 value is missing");
}

let r1: Result<i32, String> = Err("Error occurred".into());
if r1.is_ok() {
    println!("r1 value: {}", r1.unwrap());
} else {
    println!("r1 value is missing: \"{}\"", r1.unwrap_err());
}
実行結果
o1 value: 5
r1 value is missing: "Error occurred"

これらとは逆のis_none(), is_err()もあります。

unwrap_or

NoneErrのときに正常値とは別の値を設定することができます。

let o1: Option<i32> = None;
let o1_val = o1.unwrap_or(0);
println!("o1 value: {}", o1_val);

let r1: Result<i32, String> = Err("Error occurred".into());
let r1_val = r1.unwrap_or(-1);
println!("r1 value: {}", r1_val);
実行結果
o1 value: 0
r1 value: -1

当然Some(5)Ok(12)のときはそれぞれ512になります。

この他にはunwrap_or_elseなどもあります。

終わりに

OptionResultで使えるメソッドはたくさんあって使い方や名前を覚えるのが大変です。その他のメソッドについてはマニュアルを参照すると良いと思います。
記事を書くことで理解が深まった気がします。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?