rust
1let text:i32 = text.parse::<i32>().expect("Err"); 2
でtextが"hello"とかだと
thread 'main' panicked at 'Err: ParseIntError { kind: InvalidDigit }', src/main.rs:7:20 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
のようになりますが
"Err"のみをログに出すことはできますか
方法を教えて欲しいです。
よろしくお願いします。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
ベストアンサー
こんにちは。
回答の前にまずパニック(expectの中身)について認識をすりあわせたいです。
パニックは回復不能なエラー、プログラムのバグでないと起きえないことが起きたときに使う回復不能なエラーです(参考 panic!で回復不能なエラー)。他の言語でいう例外は Result
型が担当するので、例外のような気持で使うものではありません。なのでパニックを使いつつユーザフレンドリなエラーメッセージを残そうとしているときは、使い方を間違っている可能性があります。
このような前提の下で、もしやりたいことが「エラーメッセージを出して終了したい」だけならパニックを使わずに以下のような書き方があります。
rust
1use std::process::exit; 2 3let text = match text.parse::<i32>() { 4 Ok(text) => text, 5 Err(_) => { 6 // エラー出力にログを出す 7 eprintln!("Err"); 8 // プログラムをエラー終了する 9 exit(1) 10 } 11};
さらにもし、やりたいことが「"Err" をログに出す」だけが目的なら exit
を呼ぶのは不適当です。 exit
を呼んだらプログラムが終了してしまいます。
とはいえ、エラーだと数値が取り出せないので適当なエラー値を返して関数から抜けることになるかと思います。
rust
1use std::error::Error as StdError; 2use std::fmt; 3 4// 関数か抜けるときの値を用意 5#[derive(Debug)] 6enum Error { 7 UserInputError 8} 9 10impl fmt::Display for Error { 11 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 12 match self { 13 Error::UserInputError => write!(f, "Err"), 14 } 15 } 16} 17 18// エラーを実装しておくと扱いやすい 19impl StdError for Error {} 20 21// 処理する関数 22fn hogehoge(text: String) -> Result<i32, Error> { 23 let text = match text.parse::<i32>() { 24 Ok(text) => text, 25 Err(_) => { 26 eprintln!("Err"); 27 return Err(Error::UserInputError); 28 } 29 }; 30 Ok(text) 31}
ここでは文脈がないので Error
を定義して返しましたが、もし文脈的に io::Error
が適当なら io::Error
を返してもいいです。
最後に、もし本当に回復不能なエラーを使った上で、それを処理したいという特殊用途であれば std::panic::set_hook
がありますが、メッセージだけを取り出すのは難しそうでした。
rust
1use std::panic; 2 3fn main() { 4 panic::set_hook(Box::new(|panic| { 5 if let Some(payload) = panic.payload().downcast_ref::<String>() { 6 eprintln!("{}", payload); 7 } else { 8 eprintln!("{}", panic); 9 } 10 })); 11 12 let text = "hello"; 13 let text = text.parse::<i32>().expect("Err"); 14} 15
console
1Err: ParseIntError { kind: InvalidDigit }
exit
を呼ぶ方法、Error
を定義して関数から抜ける方法、パニックハンドラを使う方法の3つを提示しましたが、恐らく exit
を呼ぶ方法が無難だと思います。
複雑なことをやりはじめたら Error
を定義して使う方法に移行して下さい。 Error
を定義して使う方法は公式ドキュメントなどを参考にして下さい: Resultで回復可能なエラー
投稿2020/06/28 02:51
総合スコア468
0
std::panic::set_hookを使えば可能です。
panic!
や.expect(_)
、todo!
、unreachable!
だとPanicInfo::payloadは&'static str
かString
なので、std::any::Any::downcast_refで条件分岐すれば良いです。
rust
1use std::panic; 2 3panic::set_hook(Box::new(|panic| { 4 if let Some(payload) = panic.payload().downcast_ref::<&str>() { 5 eprintln!("{}", payload); 6 } else if let Some(payload) = panic.payload().downcast_ref::<String>() { 7 eprintln!("{}", payload); 8 } else { 9 eprintln!("{}", panic); 10 } 11})); 12 13panic!("Panic message");
text
1$ cargo run 2 Compiling a v0.0.0 (/home/ryo/src/local/scripts/rs/a) 3 Finished dev [unoptimized + debuginfo] target(s) in 0.31s 4 Running `/home/ryo/src/local/scripts/rs/target/debug/a` 5Panic message
(追記1) ParseIntError
を無視するなら.expect("メッセージ")
のかわりに.ok().expect("メッセージ")
か、.unwrap_or_else(|_| panic!("メッセージ"))
とすれば良いです。
(追記2) .ok().expect(_)はClippy (Rustツールチェインに付いてくるlint)のwarning対象のようです。
ところで前の質問でのコードでも触れましたが、main
の返り値には()
の他にResult<(), E>
(E: Debug
)が使えます。 こちらを使うことをおすすめします。
main
の返り値をResult
して実際にErr
が返される場合、Error: {Debug表記}
というエラーメッセージになります。
rust
1use std::fmt; 2 3fn main() -> Result<(), MyError> { 4 Err(MyError) 5} 6 7struct MyError; 8 9impl fmt::Debug for MyError { 10 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { 11 write!(fmt, "Debug表記") 12 } 13}
text
1$ cargo run 2 Compiling a v0.0.0 (/home/ryo/src/local/scripts/rs/a) 3 Finished dev [unoptimized + debuginfo] target(s) in 0.24s 4 Running `/home/ryo/src/local/scripts/rs/target/debug/a` 5Error: Debug表記
(Result<(), String>
やResutl<() &'static str>
とした場合、Error: "エラーメッセージ"
のように""
付きで表示されてしまいます。)
現在(特に小規模なアプリケーションでは)エラー型にanyhow::Errorを使うことが推奨されています。 anyhow::Error
のDebug
表示では中身のエラーはDebug
ではなくDisplayで表示されます。 またstd::error::Error::sourceの内容も表示してくれます。
(追記1) このコードを貼るのを忘れてました...
rust
1use anyhow::Context as _; 2use std::fs; 3 4fn main() -> anyhow::Result<()> { 5 let _content = fs::read_to_string("./nonexisting-file") 6 .with_context(|| "Failed to read ./nonexisting-file")?; 7 8 Ok(()) 9}
text
1$ cargo add anyhow 2 Updating 'https://github.com/rust-lang/crates.io-index' index 3 Adding anyhow v1.0.31 to dependencies 4$ cargo run 5 Compiling a v0.0.0 (/home/ryo/src/local/scripts/rs/a) 6 Finished dev [unoptimized + debuginfo] target(s) in 0.32s 7 Running `/home/ryo/src/local/scripts/rs/target/debug/a` 8Error: Failed to read ./nonexisting-file 9 10Caused by: 11 No such file or directory (os error 2)
投稿2020/06/27 17:30
編集2020/06/27 18:03総合スコア86
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/06/27 17:48
2020/06/28 03:07
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2020/06/28 03:05