ANimator120
ANimator120

Reputation: 3401

Rust Snafu Missing 'source' field

I'm trying to use the snafu crate for error handling, but keep getting erros that my Error enum struct is missing the 'source' and that IntoError is not implimented for Error:

//main.rs
use snafu::{ResultExt, Snafu};
#[derive(Debug, Snafu)]
#[snafu(visibility = "pub(crate)")]
pub enum Error{
    #[snafu(display("Could not load gallery JSON: {}: {}", json_path, source))]
    LoadGallery {
        source: std::io::Error,
        json_path: String,
    },
}
//gallery.rs
use snafu::{ResultExt}; 
use crate::Error::{LoadGallery};

pub struct Gallery{
    name: String,
}

impl Gallery{
    pub fn from_json(json_path: String)->Result<()>{
        let configuration = std::fs::read_to_string(&json_path).context(LoadGallery { json_path })?;
        Ok(())
    }
}

results in:

let configuration = std::fs::read_to_string(&json_path).context(LoadGallery { json_path })?;
|                                                                         ^^^^^^^^^^^ missing `source`
let configuration = std::fs::read_to_string(&json_path).context(LoadGallery { json_path })?;
|                                                                         ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `IntoError<_>` is not implemented for `Error`

Based on this example from the docs, I don't see what I'm doing wrong:

use snafu::{ResultExt, Snafu};
use std::{fs, io, path::PathBuf};

#[derive(Debug, Snafu)]
enum Error {
    #[snafu(display("Unable to read configuration from {}: {}", path.display(), source))]
    ReadConfiguration { source: io::Error, path: PathBuf },

    #[snafu(display("Unable to write result to {}: {}", path.display(), source))]
    WriteResult { source: io::Error, path: PathBuf },
}

type Result<T, E = Error> = std::result::Result<T, E>;

fn process_data() -> Result<()> {
    let path = "config.toml";
    let configuration = fs::read_to_string(path).context(ReadConfiguration { path })?;
    let path = unpack_config(&configuration);
    fs::write(&path, b"My complex calculation").context(WriteResult { path })?;
    Ok(())
}

fn unpack_config(data: &str) -> &str {
    "/some/path/that/does/not/exist"
}

Upvotes: 0

Views: 510

Answers (1)

vallentin
vallentin

Reputation: 26157

It's because when you're constructing LoadGallery, you're attempting to construct Error::LoadGallery. You then get a compile error saying "missing source", because the Error::LoadGallery variant has a source field. Fixing it is straight forward, you just need to change which LoadGallery you import.

// Not this one:
// use crate::Error::LoadGallery;

// This one:
use crate::LoadGallery;

Why? Because snafu generates a struct for each of Error's variants. So there's a struct LoadGallery being generated. This struct doesn't contain the source field, which is why you can construct it without source and pass it to context(), because it's not actually Error::LoadGallery.

Your from_json() also needs to return Result<(), Error> instead of Result<()> (you don't have the type alias, like in the example.)

use crate::{Error, LoadGallery};
use snafu::ResultExt;

pub struct Gallery {
    name: String,
}

impl Gallery {
    pub fn from_json(json_path: String) -> Result<(), Error> {
        let configuration =
            std::fs::read_to_string(&json_path).context(LoadGallery { json_path })?;
        Ok(())
    }
}

If you're curious you can use cargo expand to inspect what macros expand to. You first need to install it by doing cargo install expand. Then you can execute cargo expand in any project.

Upvotes: 1

Related Questions