Rust错误处理-为什么会给予不同的输出?

hxzsmxv2  于 2023-03-12  发布在  其他
关注(0)|答案(1)|浏览(118)

我在让miette给予一致的输出时遇到了问题。
我希望下面的程序无论传入“good”(给出我想要的花哨格式)还是“bad”(打印更像调试的错误消息)都会给予相同的输出,但有一种方法我可以打印得很漂亮,另一种方法我可以得到一个更基本的消息,我不知道为什么--这是怎么回事?
(This大量复制自本示例-注意,开头有一条dbg!()语句,我希望与结尾的Error: ...输出不同。)

use std::error::Error;

use miette::{Diagnostic, SourceSpan};
use miette::{NamedSource, Result as MietteResult};
use thiserror::Error;

#[derive(Error, Debug, Diagnostic)]
#[error("oops!")]
#[diagnostic(
    code(oops::my::bad),
    url(docsrs),
    help("try doing it better next time?")
)]
struct MyBad {
    // The Source that we're gonna be printing snippets out of.
    // This can be a String if you don't have or care about file names.
    #[source_code]
    src: NamedSource,
    // Snippets and highlights can be included in the diagnostic!
    #[label("This bit here")]
    bad_bit: SourceSpan,
}

fn this_gives_correct_formatting() -> MietteResult<()> {
    let res: Result<(), MyBad> = Err(MyBad {
        src: NamedSource::new("bad_file.rs", "source\n  text\n    here".to_string()),
        bad_bit: (9, 4).into(),
    });

    res?;

    Ok(())
}

fn main() -> Result<(), Box<dyn Error>> {
    if std::env::args().nth(1).unwrap() == "bad" {
        let res: Result<(), MyBad> = Err(MyBad {
            src: NamedSource::new("bad_file.rs", "source\n  text\n    here".to_string()),
            bad_bit: (9, 4).into(),
        });

        dbg!(&res);

        res?;

        Ok(())
    } else if std::env::args().nth(1).unwrap() == "good" {
        let res = this_gives_correct_formatting();

        dbg!(&res);

        res?;

        Ok(())
    } else {
        panic!("Pass either 'good' or 'bad'");
    }
}

在我的Cargo.toml中:

[dependencies]
miette = { version = "5.5.0", features = ["fancy"] }
thiserror = "1.0.39"

会话的输出:

$ cargo run good
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/rust-play good`
[src/main.rs:50] &res = Err(
    MyBad {
        src: NamedSource {
            name: "bad_file.rs",
            source: "<redacted>",
        ,
        bad_bit: SourceSpan {
            offset: SourceOffset(
                9,
            ),
            length: SourceOffset(
                4,
            ),
        },
    },
)
Error: oops::my::bad (https://docs.rs/rust-play/0.1.0/rust_play/struct.MyBad.html)

  × oops!
   ╭─[bad_file.rs:1:1]
 1 │ source
 2 │   text
   ·   ──┬─
   ·     ╰── This bit here
 3 │     here
   ╰────
  help: try doing it better next time?

$ cargo run bad
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target/debug/rust-play bad`
[src/main.rs:42] &res = Err(
    MyBad {
        src: NamedSource {
            name: "bad_file.rs",
            source: "<redacted>",
        ,
        bad_bit: SourceSpan {
            offset: SourceOffset(
                9,
            ),
            length: SourceOffset(
                4,
            ),
        },
    },
)
Error: MyBad { src: NamedSource { name: "bad_file.rs", source: "<redacted>", bad_bit: SourceSpan { offset: SourceOffset(9), length: SourceOffset(4) } }
u3r8eeie

u3r8eeie1#

“好”的情况会产生miette::Error类型的错误,而“坏”的情况会产生MyBad类型的错误,据推测,前一种类型具有Display实现,它会产生漂亮的人类可读输出。
请注意,?运算符不仅在Err情况下返回错误,它还尝试使用Into::into()进行转换。x?通常等效于:

match x {
    Ok(v) => v,
    Err(e) => return Err(e.into()),
}

因此,如果x的类型为Result<_, E>,函数声明的返回类型为Result<_, F>,并且E有一个Into<F>的实现,那么?操作符将透明地执行此转换,这很容易被忽略,所以您没有捕捉到它是可以理解的。
如果将main()的返回类型替换为MietteResult<()>,则会出现编译时错误,即两个条件块的返回类型不匹配。
您可以通过将“bad”情况下的错误值转换为miette::Error来解决此问题:

let res: Result<(), miette::Error> = Err(MyBad { ... }.into());

相关问题