unwrap
やらexpect
やら… いろいろあるので、まとめてみました。
Result 型のエラーハンドリング
Stirng
をu32
に型変換するparse::<u32>()
を使って、エラーハンドリングしていきます。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
let parsed: Result<u32, ParseIntError> = text.parse::<u32>();
match parsed {
Ok(number) => println!("{}", number), // <- u32
Err(err) => eprintln!("{}", err), // <- ParseIntError
}
}
$ cargo run -- 100
100
$ cargo run -- error
invalid digit found in string
expect
Ok
の場合はT
を返し、Err
の場合はメッセージとして&str
を返します。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
let number: u32 = text.parse::<u32>().expect("Error!");
println!("{}", number);
}
$ cargo run -- 100
100
$ cargo run -- error
thread 'main' panicked at 'Error!: ParseIntError { kind: InvalidDigit }', src/main.rs:7:43
expect_err
Ok
の場合に panic が発生します。OK 時に出力するメッセージ &str
は必須です。
Err
の場合、panic は起きないため正常終了します。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
let parsed: ParseIntError = text.parse::<u32>().expect_err("OK!");
println!("{:?}", parsed);
}
$ cargo run -- 100
thread 'main' panicked at 'OK!: 100', src/main.rs:7:53
$ cargo run -- error
ParseIntError { kind: InvalidDigit }
unwrap
Ok
の場合はT
を返し、Err
の場合は panic が発生します。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
let number: u32 = text.parse::<u32>().unwrap();
println!("{}", number);
}
$ cargo run -- 100
100
$ cargo run -- error
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: ParseIntError { kind: InvalidDigit }', src/main.rs:7:43
unwrap_or
Ok
の場合はT
を返し、Err
の場合はdefault: T
を返します。
以下のコードではデフォルトの値として10u32
を渡しています。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
let number: u32 = text.parse::<u32>().unwrap_or(10u32);
println!("{}", number);
}
$ cargo run -- 100
100
$ cargo run -- error
10
unwrap_or_else
Ok
の場合、引数のクロージャを実行して結果の値を返します。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
let number: u32 = text.parse::<u32>().unwrap_or_else(|e| {
eprintln!("{}", e);
10u32
});
println!("{}", number);
}
$ cargo run -- 100
100
$ cargo run -- error
invalid digit found in string
10
以下のように書くことも可能です。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
let number: u32 = text.parse::<u32>().unwrap_or_else(|_| 10u32);
println!("{}", number);
}
$ cargo run -- 100
100
$ cargo run -- error
10
unwrap_or
とunwrap_or_else
unwrap
する対象が関数である場合、遅延評価されるunwrap_or_else
を使うことが推奨されています。
unwrap_or
に渡された引数は先行評価されます。
unwrap_or_default
Err
の場合、デフォルトの値が返ります。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
let number: u32 = text.parse::<u32>().unwrap_or_default();
println!("{}", number);
}
今回はu32
なので、デフォルト値の0
が出力されます。
$ cargo run -- 100
100
$ cargo run -- error
0
ok
Option<T>
、ここではOption<String>
に変換できます。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
let parsed: Option<u32> = text.parse::<u32>().ok();
println!("{:?}", parsed);
}
$ cargo run -- 100
Some(100)
$ cargo run -- error
None
err
Option<Error>
、ここではOption<ParseIntError>
変換できます。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
let parsed: Option<ParseIntError> = text.parse::<u32>().err();
println!("{:?}", parsed);
}
$ cargo run -- 100
None
$ cargo run -- error
Some(ParseIntError { kind: InvalidDigit })
and
結果がOk
であればOk<T>
を返し、それ以外の場合はself
のErr
を返します。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
let parsed: Result<String, ParseIntError> = text.parse::<u32>().and(Ok("Success!".to_string()));
println!("{:?}", parsed);
}
$ cargo run -- 100
Ok("Success!")
$ cargo run -- error
Err(ParseIntError { kind: InvalidDigit })
and_then
結果がOk
の場合は引数で渡した関数を実行し、それ以外の場合はself
のErr
値を返します。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
let parsed: Result<u32, ParseIntError> = text.parse::<u32>().and_then(double);
println!("{:?}", parsed);
}
fn double(num: u32) -> Result<u32, ParseIntError> {
Ok(num * 2)
}
$ cargo run -- 100
Ok(200)
$ cargo run -- error
Err(ParseIntError { kind: InvalidDigit })
? 演算子
Result<T, E>
を返す関数では、?
演算子を使うことができます。
use std::num::ParseIntError;
fn main() -> Result<(), ParseIntError> {
let text = "error".to_string();
let parsed: u32 = text.parse::<u32>()?;
println!("{:?}", parsed);
Ok(())
}
$ cargo run -- 100
100
$ cargo run -- error
Error: ParseIntError { kind: InvalidDigit }
map / map_err
map()
はOk(T)
の場合のみ実行され、内部クロージャがT
を返すとmap()
がOk(T)
をラップします。
クロージャではErr
になり得る処理は使用できません。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
println!("{:?}", double(&text))
}
fn double(text: &str) -> Result<u32, ParseIntError> {
text.parse::<u32>().map(|num| num * 2)
}
$ cargo run -- 100
Ok(200)
$ cargo run -- error
Err(ParseIntError { kind: InvalidDigit })
map_err()
を組み合わせて、Err
の場合はErr(String)
を返すようにしてみます。
map_err()
では内部クロージャがT
を返すとmap_err()
がErr(T)
をラップします。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
println!("{:?}", double(&text))
}
fn double(text: &str) -> Result<u32, String> {
text.parse::<u32>().map(|num| num * 2).map_err(|e| e.to_string())
}
$ cargo run -- 100
Ok(200)
$ cargo run -- error
Err("invalid digit found in string")
is_ok
Ok
の場合、bool
型でtrue
を返します。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
// 戻り値の型は bool
if text.parse::<u32>().is_ok() {
println!("{}", "Success!");
}
}
$ cargo run -- 100
Success!
is_err
Err
の場合、bool
型でtrue
を返します。
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
// 戻り値の型は bool
if text.parse::<u32>().is_err() {
println!("{}", "Error!");
}
}
$ cargo run -- error
Error!
transpose
Result<Option<T>, E>
をOption<Result<T, E>>
に変換します。
use std::env;
fn main() {
let text = env::args().nth(1).expect("Please specify an text.");
let parsed: Option<Result<u32, ()>> = parse_u32(&text).transpose();
println!("{:?}", parsed);
}
fn parse_u32(text: &str) -> Result<Option<u32>, ()> {
match text.parse::<u32>() {
Ok(num) => {
if num > 10 {
Ok(Some(num))
} else {
Ok(None)
}
}
Err(_) => Err(()),
}
}
$ cargo run -- 100
Some(Ok(100))
$ cargo run -- 1
None
$ cargo run -- error
Some(Err(()))
if let 構文
Ok
もしくはErr
場合のみ処理したいはif let
構文を使うこともできます。
if let Some(..)
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
if let Ok(number) = text.parse::<u32>() {
println!("{}", number);
}
}
$ cargo run -- 100
100
if let Err(..)
use std::env;
use std::num::ParseIntError;
fn main() {
let text = env::args().nth(1).expect("Please specify a text.");
if let Err(err) = text.parse::<u32>() {
eprintln!("{}", err);
}
}
$ cargo run -- error
invalid digit found in string
Option 型のエラーハンドリング
環境変数から値を取得することができるenv::var_os
を使って、エラーハンドリングをしていきます。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
let s: Option<OsString> = env::var_os(&key);
println!("{:?}", s);
}
$ TEST=test cargo run
Some("test")
$ ERROR=error cargo run
None
expect
Some
の場合はT
を返し、Err
の場合はメッセージとして&str
を返します。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
let s: OsString = env::var_os(&key).expect("none");
println!("{:?}", s);
}
$ TEST=test cargo run
"test"
$ ERROR=error cargo run
thread 'main' panicked at 'none', src/main.rs:6:41
unwrap
Some
の場合はT
を返し、None
の場合は panic が発生します。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
let s: OsString = env::var_os(&key).unwrap();
println!("{:?}", s);
}
$ TEST=test cargo run
"test"
$ ERROR=error cargo run
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', src/main.rs:6:41
unwrap_or
Some
の場合はT
を返し、None
の場合はdefault: T
を返します。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
let s: OsString = env::var_os(&key).unwrap_or(OsString::from("undefined"));
println!("{:?}", s);
}
$ TEST=test cargo run
"test"
$ ERROR=error cargo run
"undefined"
unwrap_or_else
None
の場合、引数のクロージャを実行して結果の値を返します。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
let s: OsString = env::var_os(&key).unwrap_or_else(|| OsString::from("undefined"));
println!("{:?}", s);
}
$ TEST=test cargo run
"test"
$ ERROR=error cargo run
"undefined"
unwrap_or_default
None
の場合、デフォルトの値が返ります。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
let s: OsString = env::var_os(&key).unwrap_or_default();
println!("{:?}", s);
}
$ TEST=test cargo run
"test"
$ ERROR=error cargo run
""
ok_or
Option<T>
をResult<T, E>
に変換します。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
let s: Result<OsString, String> = env::var_os(&key).ok_or("undefined".to_string());
println!("{:?}", s);
}
$ TEST=test cargo run
Ok("test")
$ ERROR=error cargo run
Err("undefined")
ok_or_else
Option<T>
をResult<T, E>
に変換します。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
let s: Result<OsString, String> = env::var_os(&key).ok_or_else(|| "undefined".to_string());
println!("{:?}", s);
}
$ TEST=test cargo run
Ok("test")
$ ERROR=error cargo run
Err("undefined")
and
結果がSome
であればSome<T>
を返し、それ以外の場合はNone
を返します。
use std::env;
fn main() {
let key = "TEST".to_string();
let s: Option<String> = env::var_os(&key).and(Some("Success!".to_string()));
println!("{:?}", s);
}
$ TEST=test cargo run
Some("Success!")
$ ERROR=error cargo run
None
and_then
結果がSome
の場合は引数で渡した関数を実行し、それ以外の場合はself
のNone
を返します。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
let s: Option<String> = env::var_os(&key).and_then(change_to_string);
println!("{:?}", s);
}
fn change_to_string(target: OsString) -> Option<String> {
match target.into_string() {
Ok(s) => Some(s),
Err(_) => None,
}
}
$ TEST=test cargo run
Some("test")
$ ERROR=error cargo run
None
? 演算子
Option<T>
を返す関数では、?
演算子を使うことができます。
se std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
let s: Option<String> = env::var_os(&key).and_then(change_to_string);
println!("{:?}", s);
}
fn change_to_string(target: OsString) -> Option<String> {
let validated =match target.into_string() {
Ok(s) => Some(s),
Err(_) => None,
};
let result = validated?; // return Option<String>::None
Some(result)
}
$ TEST=test cargo run
Some("test")
$ ERROR=error cargo run
None
map
map()
はSome(T)
の場合のみ実行され、内部クロージャがT
を返すとmap()
がSome(T)
をラップします。
use std::env;
fn main() {
let key = "TEST".to_string();
let s: Option<String> = env::var_os(&key).map(|s| {
match s.into_string() {
Ok(s) => s,
Err(_) => "failed".to_string()
}
});
println!("{:?}", s);
}
$ TEST=test cargo run
Some("test")
$ TEST_ERROR=error cargo run
None
map_or
Some
の場合は関数Fの実行結果、None
の場合はdefault: U
が返ります。
デフォルト値に関数の返り値を使用する場合は遅延評価されるmap_or_else
の使用が推奨されています。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
let s: String = env::var_os(&key).map_or("default".to_string(), change_to_string);
println!("{:?}", s);
}
fn change_to_string(target: OsString) -> String {
match target.into_string() {
Ok(s) => s,
Err(_) => "failed".to_string(),
}
}
$ TEST=test cargo run
"test"
$ TEST_ERROR=error cargo run
"default"
map_or_else
Some
の場合は関数F
の実行結果、None
の場合は関数D
の実行結果が返ります。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
let s: String = env::var_os(&key).map_or_else(default_string, change_to_string);
println!("{:?}", s);
}
fn change_to_string(target: OsString) -> String {
match target.into_string() {
Ok(s) => s,
Err(_) => "failed".to_string(),
}
}
fn default_string() -> String {
"default".to_string()
}
$ TEST=test cargo run
"test"
$ TEST_ERROR=error cargo run
"default"
is_some
Some
の場合、bool
型でtrue
を返します。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
if env::var_os(&key).is_some() {
println!("Some!");
}
}
$ TEST=test cargo run
Some!
is_none
None
の場合、bool
型でtrue
を返します。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
if env::var_os(&key).is_none() {
println!("None!");
}
}
$ TEST_ERROR=error cargo run
None!
transpose
Option<Result<T, E>>
をResult<Option<T>, E>
に変換します。
use std::env;
use std::ffi::OsString;
fn main() {
let key = "TEST".to_string();
let s = env::var_os(&key).expect("TEST is undefined.");
let result: Result<Option<Strirng>, ()> = change_to_string(s).transpose();
println!("{:?}", result);
}
fn change_to_string(target: OsString) -> Option<Result<String, ()>> {
match target.into_string() {
Ok(s) => {
if !s.is_empty() {
Some(Ok(s))
}else {
Some(Err(()))
}
},
Err(_) => None,
}
}
$ TEST=test cargo run
Ok(Some("test"))
$ TEST="" cargo run
Err(())
if let 構文
Some
もしくはNone
場合のみ処理したいはif let
構文を使うこともできます。
if let Some(..)
use std::env;
fn main() {
let key = "TEST".to_string();
if let Some(s) = env::var_os(&key) {
println!("{:?}", s);
}
}
$ TEST=test cargo run
"test"
if None
use std::env;
fn main() {
let key = "TEST".to_string();
if env::var_os(&key) == None {
println!("None");
}
}
$ TEST_ERROR=error cargo run
None
まとめ
どれを使うかはケースバイケースでしょうし、その勘所は実装しながら身につける必要がありますね。
実際に手を動かすと理解が深まった気がします!
コメント