Rust错误处理:为何miette输出格式不一致?
miette库输出格式不一致问题排查
我在使用miette库时遇到输出不一致的问题:传入"good"参数执行程序能得到预期的精美格式化错误输出,但传入"bad"参数时却输出类似调试信息的基础格式,两者差异明显但找不到原因。相关代码、依赖配置及运行输出如下:
相关代码
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 { // 用于输出代码片段的源文件 // 如果不关心文件名,也可以直接用String类型 #[source_code] src: NamedSource, // 诊断信息中可包含代码片段高亮 #[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) } }
问题原因及解决方法
原因
问题核心在于错误类型的转换差异:
good分支中,this_gives_correct_formatting返回MietteResult<()>(本质是Result<(), miette::Error>),调用res?时,miette的错误处理机制会自动触发精美格式化输出。bad分支中,直接使用Result<(), MyBad>,当该错误被向上转换为Box<dyn Error>时,丢失了miette专属的格式化上下文,最终输出的是错误结构体的Debug格式。
解决方法
方法一:将错误包装为MietteResult
修改bad分支中的错误定义,将Result<(), MyBad>转换为MietteResult<()>:
let res: MietteResult<()> = Err(MyBad { src: NamedSource::new("bad_file.rs", "source\n text\n here".to_string()), bad_bit: (9, 4).into(), }.into());
方法二:修改main函数返回类型为MietteResult
直接将main函数的返回值从Result<(), Box<dyn Error>>改为MietteResult<()>,确保所有错误都通过miette机制处理:
fn main() -> MietteResult<()> { 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'"); } }
两种方法都能让"bad"分支的错误输出与"good"分支保持一致的精美格式。
内容的提问来源于stack exchange,提问作者John Graham
相关产品推荐
相关产品推荐

