Skip to content

Why isn't Whatever Send + Sync by default? #446

Open
@zardini123

Description

Hi @shepmaster! Thank you for the wonderful crate!

Problem

I have been switching my project to utilize snafu. Previously, I used anyhow::Error as a generic error type when I didn't understand how to implement error types. Now I am rewriting my project with proper error types, and in the meantime switching out anyhow::Error with snafu::Whatever.

When switching, I ran into a problem. I have a future being spawned via Tokio, where the future has a output type of Result<(), snafu::Whatever>. As this is concurrency, the rust compiler complains that that Whatever does not have Send + Sync markers for its source type. The compile error is pretty indirect about this issue. I have the error included at the bottom of this post. To figure out where this issue was coming from required a bunch of research, as shown below.

Research

Searching "snafu thread saftey" on Google returns the project http-whatever, where they essentially implement a new Whatever but with Send + Sync attached to the StdError:

https://github.com/bassmanitram/http-whatever/blob/af4fb2d672011e76a5746c27f31a2fdf65979326/src/lib.rs#L84-L93

Snafu's Whatever:

snafu/src/lib.rs

Lines 1557 to 1569 in 073cc38

#[derive(Debug, Snafu)]
#[snafu(crate_root(crate))]
#[snafu(whatever)]
#[snafu(display("{message}"))]
#[snafu(provide(opt, ref, chain, dyn std::error::Error => source.as_deref()))]
#[cfg(any(feature = "std", test))]
pub struct Whatever {
#[snafu(source(from(Box<dyn std::error::Error>, Some)))]
#[snafu(provide(false))]
source: Option<Box<dyn std::error::Error>>,
message: String,
backtrace: Backtrace,
}

I found a snafu issue from January 2022 that asks for implementation details for making a custom snafu error type be async compatible. From there in May 2022 a PR was merged that showed an error type with async/multi-threading as part of the tests. This is part of the tests, but not part of the examples, so finding this example required a bunch of searching as its not part of the doc examples.

In addition, anyhow::Error has Sync + Send markers (source in anyhow code, source in anyhow docs).

Question

Why isn't Whatever Send + Sync by default?

I could implement a new Whatever as done by http-whatever and as shown in the snafu test. But, if we consider snafu::Whatever to be a catch-all error type until the user can make more specific types, shouldn't we expect as a user that it can be used in all places including async and multi-threading? Is there a detail I am missing that makes Whatever not able to be Send + Sync compatible by default?

Thank you!


Whatever Send + Sync error report

error[E0277]: `(dyn StdError + 'static)` cannot be sent between threads safely
    --> src\file\file.rs:435:88
     |
435  |   ...                   task = Some(Handle::current().spawn(async move {
     |  ____________________________________________________________________-----_^
     | |                                                                    |
     | |                                                                    required by a bound introduced by this call
...    |
452  | | ...                       return Ok::<(), snafu::Whatever>(());
453  | | ...                   }));
     | |_______________________^ `(dyn StdError + 'static)` cannot be sent between threads safely
     |
     = help: the trait `std::marker::Send` is not implemented for `(dyn StdError + 'static)`
     = note: required for `Unique<(dyn StdError + 'static)>` to implement `std::marker::Send`
note: required because it appears within the type `Box<(dyn StdError + 'static)>`
    --> C:\Users\zardini123\.rustup\toolchains\1.76.0-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\alloc\src\boxed.rs:195:12
     |
195  | pub struct Box<
     |            ^^^
note: required because it appears within the type `std::option::Option<Box<(dyn StdError + 'static)>>`
    --> C:\Users\zardini123\.rustup\toolchains\1.76.0-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\option.rs:570:10
     |
570  | pub enum Option<T> {
     |          ^^^^^^
note: required because it appears within the type `Whatever`
    --> C:\Users\zardini123\.cargo\registry\src\index.crates.io-6f17d22bba15001f\snafu-0.8.2\src\lib.rs:1563:12
     |
1563 | pub struct Whatever {
     |            ^^^^^^^^
note: required because it appears within the type `Result<(), Whatever>`
    --> C:\Users\zardini123\.rustup\toolchains\1.76.0-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\result.rs:502:10
     |
502  | pub enum Result<T, E> {
     |          ^^^^^^
note: required by a bound in `Handle::spawn`
    --> C:\Users\zardini123\.cargo\registry\src\index.crates.io-6f17d22bba15001f\tokio-1.36.0\src\runtime\handle.rs:189:20
     |
186  |     pub fn spawn<F>(&self, future: F) -> JoinHandle<F::Output>
     |            ----- required by a bound in this associated function
...
189  |         F::Output: Send + 'static,
     |                    ^^^^ required by this bound in `Handle::spawn`

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions