Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

David Rickard
David Rickard

Posted on

     

Using error-stack with Tauri

The problem

Rust's built-in error handling is rather bare-bones and requires a whole lot of effort to preserve a proper stack trace, where other languages give you that for free.

In order to show the inner error along with your error, you need to wrap the inner error explicitly:

#[derive(Debug)]enumServiceError{Json(serde_json::Error),Http(reqwest::Error),}
Enter fullscreen modeExit fullscreen mode

You then need to implementDisplay with case arms for each enum:

implfmt::DisplayforServiceError{fnfmt(&self,f:&mutfmt::Formatter)->fmt::Result{match*self{ServiceError::Json(..)=>write!(f,"Could not parse the JSON"),ServiceError::Http(..)=>write!(f,"The HTTP call failed"),}}}
Enter fullscreen modeExit fullscreen mode

And then implementFrom to allow the inner error types to convert to your error:

implFrom<serde_json::Error>forServiceError{fnfrom(err:serde_json::Error)->ServiceError{ServiceError::Json(err)}}implFrom<reqwest::Error>forServiceError{fnfrom(err:reqwest::Error)->ServiceError{ServiceError::Http(err)}}
Enter fullscreen modeExit fullscreen mode

Basic error-stack support

Theerror-stack crate makes this a lot better. Its error is aReport, which stores the chain of errors it's encountered. That means you can define an error like this:

#[derive(Debug)]structServiceError;implfmt::DisplayforServiceError{fnfmt(&self,f:&mutfmt::Formatter)->fmt::Result{f.write_str("There was an error calling the service.")}}implErrorforServiceError{}
Enter fullscreen modeExit fullscreen mode

Then convert the error withinto_report():

useerror_stack::{Result,IntoReport};fnparse_it(value:&str)->Result<(),ServiceError>{letparsed:serde_json::Value=serde_json::from_str(value).into_report().change_context(ServiceError)?;println!("We parsed: {}",parsed);Ok(())}
Enter fullscreen modeExit fullscreen mode

Theinto_report() packages up an outside error into aResult, andchange_context() adds our own error type as another link in the error chain. You can add another link by callingchange_context() again with a different error type. No more annoyingFrom implemenations!

TheReport prints out beautifully with all information:

Error: experiment error: could not run experiment├╴at examples/demo.rs:51:18├╴unable to set up experiments│├─▶ invalid experiment description│   ├╴at examples/demo.rs:21:10│   ╰╴experiment 2 could not be parsed│╰─▶ invalid digit found in string    ├╴at examples/demo.rs:19:10    ├╴backtrace with 31 frames (1)    ╰╴"3o" could not be parsed as experiment━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━backtrace no. 1   0: std::backtrace_rs::backtrace::libunwind::trace             at /rustc/e972bc8083d5228536dfd42913c8778b6bb04c8e/library/std/src/../../backtrace/src/backtrace/libunwind.rs:93:5   1: std::backtrace_rs::backtrace::trace_unsynchronized             at /rustc/e972bc8083d5228536dfd42913c8778b6bb04c8e/library/std/src/../../backtrace/src/backtrace/mod.rs:66:5   2: std::backtrace::Backtrace::create             at /rustc/e972bc8083d5228536dfd42913c8778b6bb04c8e/library/std/src/backtrace.rs:332:13   3: core::ops::function::FnOnce::call_once             at /rustc/e972bc8083d5228536dfd42913c8778b6bb04c8e/library/core/src/ops/function.rs:250:5   4: core::bool::<impl bool>::then             at /rustc/e972bc8083d5228536dfd42913c8778b6bb04c8e/library/core/src/bool.rs:71:24   5: error_stack::report::Report<C>::from_frame             at ./src/report.rs:288:25   6: error_stack::report::Report<C>::new             at ./src/report.rs:274:9   7: error_stack::context::<impl core::convert::From<C> for error_stack::report::Report<C>>::from             at ./src/context.rs:83:9   8: <core::result::Result<T,E> as error_stack::result::IntoReport>::into_report             at ./src/result.rs:203:31   (For this example: additional frames have been removed)
Enter fullscreen modeExit fullscreen mode

You can attach other bits of arbitrary data to the report as well.

Tauri integration

Tauri will not let you directly return aerror_stack::Result from a#[tauri::command]. If you try, it will complain:

no methodblocking_kind on type&Result<(), Report<ServiceError>>

Tauri lets you return anything that implementsSerialize, but aReport does not. To get around this we can use a wrapper:

#[derive(Debug,Serialize)]pubstructCommandError(String);impl<C>From<Report<C>>forCommandError{fnfrom(err:Report<C>)->CommandError{CommandError(format!("{:?}",err))}}implfmt::DisplayforCommandError{fnfmt(&self,f:&mutfmt::Formatter)->fmt::Result{f.write_str(&self.0)}}
Enter fullscreen modeExit fullscreen mode

That allows us to easily map ourReports to an error type that Tauri can send to the frontend:

#[tauri::command]asyncfncall_the_service()->core::result::Result<(),CommandError>{my_module::call_service().await?;Ok(())}
Enter fullscreen modeExit fullscreen mode

That will be visible in the Dev console for the frontend page.{:?} is the debug format that includes all error information; if you want to obfuscate for production, you'll want to use{:#}.

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

  • Joined

More fromDavid Rickard

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp