shepmaster / snafu

Easily assign underlying errors into domain-specific errors while adding context
https://docs.rs/snafu/
Apache License 2.0
1.39k stars 60 forks source link

`ensure` may require hinting error type #315

Open tustvold opened 2 years ago

tustvold commented 2 years ago

I'm not sure if this is a bug or not, but I thought I'd document it as I ran into it and it caused me some confusion.

175 added an Into conversion to the ensure macro, unfortunately this means that ensure no longer helps deduce the type of a closure.

In particular this doesn't compile

use snafu::{ensure, Snafu};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("foo"))]
    Foo { val: i32 },
}

#[test]
fn test() {
    let r: Vec<_> = (0..1).map(|val| {
        ensure!(val == 0, Foo { val });
        Ok(())
    }).collect();
}

It requires you to explicitly indicate the type

use snafu::{ensure, Snafu};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("foo"))]
    Foo { val: i32 },
}

#[test]
fn test() {
    let r: Vec<_> = (0..1).map(|val| {
        ensure!(val == 0, Foo { val });
        Ok::<_, Error>(())
    }).collect();
}

This is perhaps a little surprising.

The circumstance I encountered this in was actually a little bit more confusing, where adding a contextless variant caused the error to manifest in code I hadn't touched.

For example, this fails to compile

use snafu::{ensure, Snafu};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("foo"))]
    Foo { val: i32 },

    #[snafu(context(false))]
    From { source: std::io::Error },
}

#[test]
fn test() -> Result<(), Error> {
    (0..1).try_for_each(|val| {
        ensure!(val == 0, Foo { val });
        Ok(())
    })?;
    Ok(())
}

But does without the contextless variant

use snafu::{ensure, Snafu};

#[derive(Debug, Snafu)]
pub enum Error {
    #[snafu(display("foo"))]
    Foo { val: i32 },
}

#[test]
fn test() -> Result<(), Error> {
    (0..1).try_for_each(|val| {
        ensure!(val == 0, Foo { val });
        Ok(())
    })?;
    Ok(())
}

I don't really know what the solution here is, ensure is behaving a lot like ? operator and so I'm not sure the current behavior is wrong per-se, but thought I'd document this

iago-lito commented 4 months ago

I've stumbled accross the following error today, and I suppose it's maybe related?

use snafu::{ensure, Snafu};

#[derive(Debug, Snafu)]
enum Error {
    A { int: u64 },
}

fn f(ok: bool) -> Result<(), Error> {
    ensure!(ok, ASnafu { int: 0 }); // << Error [E0277] the trait bound `u64: std::convert::From<i32>` is not satisfied.
    Ok(())
}

.. or should I open a new issue about this?

The solution/workaround is to use 0u64 instead in my situation. So maybe in your situation @tustvold, using (0..1i32) instead of just (0..1) could help?