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 stringexpect
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:43expect_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:43unwrap_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
10unwrap_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
10unwrap_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
0ok
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
Noneerr
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
100if 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 stringOption 型のエラーハンドリング
環境変数から値を取得することができる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
Noneexpect
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:41unwrap
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:41unwrap_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
Noneand_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
Nonemap
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
Nonemap_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まとめ
どれを使うかはケースバイケースでしょうし、その勘所は実装しながら身につける必要がありますね。
実際に手を動かすと理解が深まった気がします!


コメント