I want to deserialize a configuration structure like this:
#[deriving(Clone, PartialEq, Decodable, Show)]
pub struct Config {
network1: Network,
network2: Network
}
#[deriving(Clone, PartialEq, Decodable, Show)]
pub struct Network {
interface: Option<String>,
prefer: Option<AddressPreference>,
address: Option<String>,
port: u16
}
#[deriving(Clone, PartialEq, Show)]
pub enum AddressPreference {
IPv4, IPv6, Any
}
It forms a tree of structs and enums. But the decoder I want to use does not support enums, so I thought that I will be able to represent the enum as a string by implementing Decodable for the enum which matches on strings. But I don't know how to do it properly.
The main problem is that I don't know how to write Decodable implementation which handles errors correctly. If I write
impl<E, D: Decoder<E>> Decodable<D, E> for AddressPreference { ... }
the only way I can indicate parsing error is fail!() - I don't know actual error type, so I can't create its instance. This is unacceptable.
Buf if I write
impl Decodable<toml::Decoder, toml::Error> for AddressPreference { ... }
that is, binding Decodable instance to concrete decoder type (which is fine for me), I lose automatic deriving of outer structures, because they are implemented for generic decoder and error:
src/sfc/config.rs:11:30: 11:39 error: expected serialize::serialize::Decodable<__D,__E>, but found serialize::serialize::Decodable<rust-toml::Decoder,rust-toml::Error> (expected type parameter but found struct rust-toml::Decoder)
src/sfc/config.rs:11 #[deriving(Clone, PartialEq, Decodable, Show)]
What should I do in this case? I guess, the proper answer in this particular case is submitting a pull request to the library enabling support for enums serialization, but I think that the question is more broad. What it the library supported enums, but I wanted to customize decoding nonetheless?
Related
I have a data format using custom enum of values. From the database I receive a Vec<MyVal>. I want to convert this to a struct (and fail if it doesn't work). I want to use serde because after processing I want to return the API response as a json, and serde makes this super easy.
Playground link for the example
enum MyVal {
Bool(bool),
Text(String)
}
#[derive(Serialize, Deserialize)]
struct User {
name: String,
registered: bool
}
The challenge is with converting the data format into the serde data model. For this I can implement a Deserializer and implement the visit_seq method i.e. visit the Vec<MyVal> as if it were a sequence and return the values one by one. The visitor for User can consume the visited values to build the struct User.
However I'm not able to figure out how to convert the Vec into something visitor_seq can consume. Here's some sample code.
struct MyWrapper(Vec<MyVal>);
impl<'de> Deserializer<'de> for MyWrapper {
type Error = de::value::Error;
// skip unncessary
fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: serde::de::Visitor<'de>,
{
let convert_myval = move |myval: &MyVal| match myval {
MyVal::Bool(b) => visitor.visit_bool(*b),
MyVal::Text(s) => visitor.visit_string(s.clone())
};
// now I have a vec of serde values
let rec_vec: Vec<Result<V::Value, Self::Error>> =
self.0.iter().map(|myval| convert_myval(myval)).collect();
// giving error!!
visitor.visit_seq(rec_vec.into_iter())
}
}
The error is
92 | visitor.visit_seq(rec_vec.into_iter())
| --------- ^^^^^^^^^^^^^^^^^^^ the trait `SeqAccess<'de>` is not implemented for `std::vec::IntoIter<Result<<V as Visitor<'de>>::Value, _::_serde::de::value::Error>>`
| |
| required by a bound introduced by this call
So I looked into SeqAccess and it has an implementor that requires that whatever is passed to it implement the Iterator trait. But I thought I had that covered, because vec.into_iter returns an IntoIter, a consuming iterator which does implement the Iterator trait.
So I'm completely lost as to what's going wrong here. Surely there must be a way to visit a Vec<Result<Value, Error>> as a seq?
Preface: The question wants to treat a Rust data structure Vec<MyData> like a serialized piece of data (e.g.: like a JSON string) and allow deserializing that into any other Rust data structure that implements Deserialize. This is a quite unusual, but not without precedent. And since the MyVals are actually pieces of data with various types which get returned from a database access crate, this approach does make sense.
The main problem with the code in the question is that it tries to deserialize two different data structures (MyWrapper<Vec<MyVal>> and MyVal) with a single Deserializer. The obvious way out is to define a second struct MyValWrapper(MyVal) and implement Deserializer for it:
struct MyValWrapper(MyVal);
impl<'de> Deserializer<'de> for MyValWrapper {
type Error = de::value::Error;
fn deserialize_any<V>(self, visitor: V) -> Result<V::Value, Self::Error>
where
V: de::Visitor<'de>,
{
match self.0 {
MyVal::Bool(b) => visitor.visit_bool(b),
MyVal::Text(s) => visitor.visit_string(s.clone()),
}
}
// Maybe you should call deserialize_any from all other deserialize_* functions to get a passable error message, e.g.:
forward_to_deserialize_any! {
bool i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 char str string
bytes byte_buf option unit unit_struct newtype_struct seq tuple
tuple_struct map struct enum identifier ignored_any
}
// Though maybe you want to handle deserialize_enum differently...
}
MyValWrapper now can deserialize a MyVal. To use MyValWrapper to deserialize a Vec of MyVals, the serde::value::SeqDeserializer adapter is convenient, it can turn an iterator of (Into)Deserializer into a sequence Deserializer:
let data: Vec<MyData> = …;
// Vec deserialization snippet
let mut sd = SeqDeserializer::new(data.into_iter().map(|myval| MyValWrapper(myval)));
let res = visitor.visit_seq(&mut sd)?;
sd.end()?;
Ok(res)
For some reason, SeqDeserializer requires the iterator items to be IntoDeserializer, but no impl<T: IntoDeserializer> Deserializer for T exists, so we need to make sure that MyValWrapper is not only a Deserializer but trivially also an IntoDeserializer:
impl<'de> IntoDeserializer<'de> for MyValWrapper {
type Deserializer = MyValWrapper;
fn into_deserializer(self) -> Self::Deserializer {
self
}
}
Finally, you need to impl Deserializer for MyWrapper (you can use the "Vec deserialization snippet" for that) — if you do actually need Deserializer to be implemented, which I suspect you don't: the SeqDeserializer already implements Deserializer, and it is a wrapper struct (just as MyWrapper is a wrapper struct). Especially, if your final goal is having a function like
fn turn_bad_myvals_into_good_T<T: DeserializeOwned>(v: Vec<MyVal>) -> T {
T::deserialize(SeqDeserializer::new(
v.into_iter().map(|myval| MyValWrapper(myval)),
))
}
then you entirely don't need MyWrapper.
Disclaimer: I'm a beginner with rust!
Lets assume I have the following to structs and I want the default json serialization from the derived Serialize traits
#[derive(Serialize)]
struct Address {
zip:usize,
street: String,
}
#[derive(Serialize)]
struct Person{
name: String,
age: usize,
address: Address
}
In addition, I have implemented a custom serde::Serializer for a legacy format which takes any Serialize and internally forwards to the serialize methods when it encounters nested structs, enums, etc.
struct MySerializer {...}
impl Serializer for MySerializer {...}
Further, I defined a custom trait MyFormat that extends the Serialize trait and is supposed to work as a tag or something, i.e. if MySerializer encounters something that implements MyFormat, it should not use Serialize, but MyFormat instead.
trait MyFormat: Serialize {
/// by default just use the normal Serialize trait method.
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
Serialize::serialize(&self, serializer)
}
}
impl MyFormat for Address {
fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
where
S: Serializer,
{
// this can actually look much more complicated, e.g. some additional mapping will be done here.
serializer.serialize_some(&self.zip)?;
serializer.serialize_some(&"some extra field only present in this format")?;
serializer.serialize_some(&self.street)?
}
}
impl MyFormat for Person {}
Now the problem is, when I use the custom serializer, I can, on the most outer level control whether to use the derived Serialize, however, the moment I descent into the structure, the Serializer trait expects Serialize types in the trait method specification which I cannot narrow down to MyFormat, as that would violate the trait.
In the above example name and age are ok, as the format for primitive types is identical, however, the nested Address might require to "inject" some additional fields if serialized to MyFormat.
I assume I just miss some paradigm that lets me do that with traits in rust. In C++ I could use template specialization or SFINAE. Is there anything in rust that can solve this kind of problem?
Kind regards,
Marti
I'm writing a function that could return several one of several different errors.
fn foo(...) -> Result<..., MyError> {}
I'll probably need to define my own error type to represent such errors. I'm presuming it would be an enum of possible errors, with some of the enum variants having diagnostic data attached to them:
enum MyError {
GizmoError,
WidgetNotFoundError(widget_name: String)
}
Is that the most idiomatic way to go about it? And how do I implement the Error trait?
You implement Error exactly like you would any other trait; there's nothing extremely special about it:
pub trait Error: Debug + Display {
fn description(&self) -> &str { /* ... */ }
fn cause(&self) -> Option<&Error> { /* ... */ }
fn source(&self) -> Option<&(Error + 'static)> { /* ... */ }
}
description, cause, and source all have default implementations1, and your type must also implement Debug and Display, as they are supertraits.
use std::{error::Error, fmt};
#[derive(Debug)]
struct Thing;
impl Error for Thing {}
impl fmt::Display for Thing {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Oh no, something bad went down")
}
}
Of course, what Thing contains, and thus the implementations of the methods, is highly dependent on what kind of errors you wish to have. Perhaps you want to include a filename in there, or maybe an integer of some kind. Perhaps you want to have an enum instead of a struct to represent multiple types of errors.
If you end up wrapping existing errors, then I'd recommend implementing From to convert between those errors and your error. That allows you to use try! and ? and have a pretty ergonomic solution.
Is that the most idiomatic way to go about it?
Idiomatically, I'd say that a library will have a small (maybe 1-3) number of primary error types that are exposed. These are likely to be enumerations of other error types. This allows consumers of your crate to not deal with an explosion of types. Of course, this depends on your API and whether it makes sense to lump some errors together or not.
Another thing to note is that when you choose to embed data in the error, that can have wide-reaching consequences. For example, the standard library doesn't include a filename in file-related errors. Doing so would add overhead to every file error. The caller of the method usually has the relevant context and can decide if that context needs to be added to the error or not.
I'd recommend doing this by hand a few times to see how all the pieces go together. Once you have that, you will grow tired of doing it manually. Then you can check out crates which provide macros to reduce the boilerplate:
error-chain
failure
quick-error
Anyhow
SNAFU
My preferred library is SNAFU (because I wrote it), so here's an example of using that with your original error type:
use snafu::prelude::*; // 0.7.0
#[derive(Debug, Snafu)]
enum MyError {
#[snafu(display("Refrob the Gizmo"))]
Gizmo,
#[snafu(display("The widget '{widget_name}' could not be found"))]
WidgetNotFound { widget_name: String },
}
fn foo() -> Result<(), MyError> {
WidgetNotFoundSnafu {
widget_name: "Quux",
}
.fail()
}
fn main() {
if let Err(e) = foo() {
println!("{}", e);
// The widget 'Quux' could not be found
}
}
Note I've removed the redundant Error suffix on each enum variant. It's also common to just call the type Error and allow the consumer to prefix the type (mycrate::Error) or rename it on import (use mycrate::Error as FooError).
1 Before RFC 2504 was implemented, description was a required method.
The crate custom_error allows the definition of custom error types with less boilerplate than what was proposed above:
custom_error!{MyError
Io{source: io::Error} = "input/output error",
WidgetNotFoundError{name: String} = "could not find widget '{name}'",
GizmoError = "A gizmo error occurred!"
}
Disclaimer: I am the author of this crate.
Is that the most idiomatic way to go about it? And how do I implement the Error trait?
It's a common way, yes. "idiomatic" depends on how strongly typed you want your errors to be, and how you want this to interoperate with other things.
And how do I implement the Error trait?
Strictly speaking, you don't need to here. You might for interoperability with other things that require Error, but since you've defined your return type as this enum directly, your code should work without it.
I am currently trying to write a custom error type for my CLI application. Now I want to write an implementation of the From trait so my custom error type can wrap all third party library errors that can occur.
The error enum:
#[derive(Debug)] // Allow the use of "{:?}" format specifier
pub enum CustomError {
Git(git2::Error),
Other
}
Now I want to implement the From Trait for the git2::Error from the git2 library to use the ? operator in my functions.
impl From<(git2::Error)> for CustomError {
fn from(cause: git2::Error) -> Self {
CustomError::Git(cause)
}
}
But when I try to use my custom error to map an error like this:
let repo = Repository::open(path).map_err(|err| CustomError::Git)?;
I am getting the following error message:
the trait `std::convert::From<fn(git2::error::Error) -> error::CustomError {error::CustomError::Git}>` is not implemented for `error::CustomError `
Can anyone help me to understand why I am getting this error and how to solve this problem ?
Any help is appreciated
You've mixed up a whole bunch of concepts; let's see if we can walk through this together and hopefully clarify all of it.
The git2 crate has its own error type, that you no doubt have discovered. Your definition of custom errors is fine as well.
The issue is twofold:
Your implementation of From<_>
From<E> allows you to transform a type from one type to another by providing the translation function (from()).
Your implementation of this was the following:
impl From<(git2::Error)> for CustomError {
fn from(cause: git2::Error) -> Self {
CustomError::Git(cause)
}
}
Brackets in rust aren't added where they should not be, and this is precisely one of the cases where this is the case. By doing this, you've actually defined From<(T)>, not From<T>. That's mistake #1.
The correct implementation simply drops the brackets:
impl From<git2::Error> for CustomError {
fn from(cause) -> Self {
CustomError::Git(cause)
}
}
Your actual conversion
Not an error per se, but a completely unnecessary operation as the ? operator handles it for you. There is no need for the map_err(), and if there was you'd be using into() rather than hard-calling the type (which should already be defined as a type in your function).
Remember, the whole point of conversion traits is to define them so you don't have to explicitly call them.
A final "demo" version of the code in working order could look like this:
extern crate git2;
use git2::Repository;
#[derive(Debug)] // Allow the use of "{:?}" format specifier
pub enum CustomError {
Git(git2::Error),
Other
}
impl From<(git2::Error)> for CustomError {
fn from(cause: git2::Error) -> Self {
CustomError::Git(cause)
}
}
fn test() -> Result<(), CustomError> {
let path = "foo";
let output = Repository::open(path)?;
Ok(())
}
fn main() {
println!("Hello, world!");
}
I'm writing a function that could return several one of several different errors.
fn foo(...) -> Result<..., MyError> {}
I'll probably need to define my own error type to represent such errors. I'm presuming it would be an enum of possible errors, with some of the enum variants having diagnostic data attached to them:
enum MyError {
GizmoError,
WidgetNotFoundError(widget_name: String)
}
Is that the most idiomatic way to go about it? And how do I implement the Error trait?
You implement Error exactly like you would any other trait; there's nothing extremely special about it:
pub trait Error: Debug + Display {
fn description(&self) -> &str { /* ... */ }
fn cause(&self) -> Option<&Error> { /* ... */ }
fn source(&self) -> Option<&(Error + 'static)> { /* ... */ }
}
description, cause, and source all have default implementations1, and your type must also implement Debug and Display, as they are supertraits.
use std::{error::Error, fmt};
#[derive(Debug)]
struct Thing;
impl Error for Thing {}
impl fmt::Display for Thing {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Oh no, something bad went down")
}
}
Of course, what Thing contains, and thus the implementations of the methods, is highly dependent on what kind of errors you wish to have. Perhaps you want to include a filename in there, or maybe an integer of some kind. Perhaps you want to have an enum instead of a struct to represent multiple types of errors.
If you end up wrapping existing errors, then I'd recommend implementing From to convert between those errors and your error. That allows you to use try! and ? and have a pretty ergonomic solution.
Is that the most idiomatic way to go about it?
Idiomatically, I'd say that a library will have a small (maybe 1-3) number of primary error types that are exposed. These are likely to be enumerations of other error types. This allows consumers of your crate to not deal with an explosion of types. Of course, this depends on your API and whether it makes sense to lump some errors together or not.
Another thing to note is that when you choose to embed data in the error, that can have wide-reaching consequences. For example, the standard library doesn't include a filename in file-related errors. Doing so would add overhead to every file error. The caller of the method usually has the relevant context and can decide if that context needs to be added to the error or not.
I'd recommend doing this by hand a few times to see how all the pieces go together. Once you have that, you will grow tired of doing it manually. Then you can check out crates which provide macros to reduce the boilerplate:
error-chain
failure
quick-error
Anyhow
SNAFU
My preferred library is SNAFU (because I wrote it), so here's an example of using that with your original error type:
use snafu::prelude::*; // 0.7.0
#[derive(Debug, Snafu)]
enum MyError {
#[snafu(display("Refrob the Gizmo"))]
Gizmo,
#[snafu(display("The widget '{widget_name}' could not be found"))]
WidgetNotFound { widget_name: String },
}
fn foo() -> Result<(), MyError> {
WidgetNotFoundSnafu {
widget_name: "Quux",
}
.fail()
}
fn main() {
if let Err(e) = foo() {
println!("{}", e);
// The widget 'Quux' could not be found
}
}
Note I've removed the redundant Error suffix on each enum variant. It's also common to just call the type Error and allow the consumer to prefix the type (mycrate::Error) or rename it on import (use mycrate::Error as FooError).
1 Before RFC 2504 was implemented, description was a required method.
The crate custom_error allows the definition of custom error types with less boilerplate than what was proposed above:
custom_error!{MyError
Io{source: io::Error} = "input/output error",
WidgetNotFoundError{name: String} = "could not find widget '{name}'",
GizmoError = "A gizmo error occurred!"
}
Disclaimer: I am the author of this crate.
Is that the most idiomatic way to go about it? And how do I implement the Error trait?
It's a common way, yes. "idiomatic" depends on how strongly typed you want your errors to be, and how you want this to interoperate with other things.
And how do I implement the Error trait?
Strictly speaking, you don't need to here. You might for interoperability with other things that require Error, but since you've defined your return type as this enum directly, your code should work without it.