top | item 42463933

(no title)

dmart | 1 year ago

Using #[from] in a thiserror enum is an antipattern, IMO. I kind of wish it wasn't included at all because it leads people to this design pattern where errors are just propagated upwards without any type differentiation or additional context.

You can absolutely have two different enum variants from the same source type. It would look something like:

    #[derive(Debug, Error)]
    pub(crate) enum MyErrorType {
        #[error("failed to create staging directory at {}", path.display())]
        CreateStagingDirectory{
            source: std::io::Error,
            path: std::path::PathBuf,
        },

        #[error("failed to copy files to staging directory")]
        CopyFiles{
            source: std::io::Error,
        }
    }
This does mean that you need to manually specify which error variant you are returning rather than just using ?:

    create_dir(path).map_err(|err| MyErrorType::CreateStagingDirectory {
        source: err, path: path.clone() 
    })?;
but I would argue that that is the entire point of defining a specific error type. If you don't care about the context and only that an io::Error occurred, then just return that directly or use a type-erased error.

discuss

order

shepmaster|1 year ago

This is one of the things I like about SNAFU: it makes this preferred pattern the default and makes it nicer to use. For example, your usage would look something like this with SNAFU:

    create_dir(path).context(CreateStagingDirectorySnafu { path })?;
Note a few points:

1. No need to use the closure

2. No need to carry the source error over yourself (`context` does this for you)

3. No need to explicitly call `clone` on the path (`context` does this for you)