-
Notifications
You must be signed in to change notification settings - Fork 61
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce constructor functions to improve ergonomics of creating error contexts #465
Comments
another example: use std::str::FromStr;
use bar::Bar;
use snafu::prelude::*;
#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("foo {bar}"))]
Foo {
bar: String,
source: bar::OtherError,
},
}
mod bar {
use super::*;
#[derive(Debug, Snafu)]
pub enum OtherError {
#[snafu(display("context {context}"))]
OtherError { context: String },
}
pub struct Bar;
impl FromStr for Bar {
type Err = OtherError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"bar" => Ok(Bar),
// now
_ => OtherSnafu {
context: "some context".to_string(),
}
.fail(),
// more concise
_ => OtherSnafu::fail("some context"),
}
}
}
}
fn main() -> Result<(), Error> {
//now
let b = "bar".parse::<Bar>().context(FooSnafu {
bar: "some context".to_string(),
})?;
// more concise
let b = "bar"
.parse::<Bar>()
.context(FooSnafu::new("some context"))?;
Ok(())
} |
In the first example, the idiomatic form of creating context is this. self.id.to_string().parse().context(ParseStrategyKindSnafu {
strategy_id: self.id,
}) (For this to work you need to rename the field On the second example, fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"bar" => Ok(Bar),
_ => OtherErrorSnafu {
context: "some context",
}.fail(),
}
} This should hopefully reduce the need for a selector constructor function. The few cases which would benefit from such a function are for error variants with a large number of variants, which are also unambiguous in their order (the literal struct construction syntax has better safeguards than function parameter listings). |
use std::str::FromStr;
use snafu::prelude::*;
#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("foo {bar}"))]
Foo { bar: String, source: String },
}
pub struct Bar;
impl FromStr for Bar {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"bar" => Ok(Bar),
_ => Err("some context".to_string()),
}
}
}
fn main() -> Result<(), Error> {
let _b = "bar".parse::<Bar>().context(FooSnafu {
bar: "some context".to_string(),
})?;
Ok(())
} error[E0599]: the method
|
@Enet4 selector constructor function can be option in |
In that failing example you seem to be using use std::str::FromStr;
use snafu::prelude::*;
#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("foo {bar}"))]
Foo {
bar: String,
source: ParseBarError
},
}
pub struct Bar;
#[derive(Debug, Snafu)]
pub enum ParseBarError {
/// totally not a bar
NotBar,
}
impl FromStr for Bar {
type Err = ParseBarError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"bar" => Ok(Bar),
_ => NotBarSnafu.fail(),
}
}
}
#[snafu::report]
fn main() -> Result<(), Error> {
let _b = "derp".parse::<Bar>().context(FooSnafu {
bar: "some context",
})?;
Ok(())
} The output:
(note how some errors might not need extra fields if the variants are well outlined) |
One other problem I am seeing with this proposal is that the methods |
@Enet4 |
Right. One question is "how much does this crate want to encourage poor practices from the rest of the ecosystem?" My immediate answer with little thought put into it is "not very much".
You can also use use snafu::{prelude::*, FromString as _, Whatever}; // Add imports
use std::str::FromStr;
#[derive(Debug, Snafu)]
pub enum Error {
#[snafu(display("foo {bar}"))]
Foo { bar: String, source: Whatever }, // Switch from `String` to `Whatever`
}
pub struct Bar;
impl FromStr for Bar {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"bar" => Ok(Bar),
_ => Err("some context".to_string()),
}
}
}
fn main() -> Result<(), Error> {
let _b = "bar"
.parse::<Bar>()
.map_err(Whatever::without_source) // Convert `String` to a `Whatever` which implements `Error`
.context(FooSnafu {
bar: "some context".to_string(),
})?;
Ok(())
} Perhaps Also note that this is a bad practice: FooSnafu {
bar: "some context".to_string(),
} This always constructs the let bar = "bar";
let _b = bar
.parse::<Bar>()
.map_err(Whatever::without_source)
.context(FooSnafu { bar })?; |
Thank you for awesome crate! use it every day in every project.
now
more concise
The text was updated successfully, but these errors were encountered: