How can I mass implement Deserialize for all types that implement a specific trait?
I am deserializing a YAML config file with Serde. For most structs I deserialize into, things are quite simple — there's a one-to-one relationship between the fields of the structs and the properties in my YAML file.
In a few cases, things are a bit more complicated. For these, the properties in the YAML file are better viewed as parameters to the constructor. The actual struct will have different fields, calculated from those.
For these cases, I have written separate config structs that I deserialize into. For simplicity, consider this silly example:
struct Message {
text: String,
}
impl Message {
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
To have Serde do the conversion from MessageConfig
to Message
for me, I implemented Deserialize
for Message
:
impl<'de> Deserialize<'de> for Message {
fn deserialize<D>(deserializer: D) -> Result<Message, D::Error>
where
D: Deserializer<'de>,
{
MessageConfig::deserialize(deserializer).map(|config| Message::from_config(config))
}
}
This works, but there would be a lot of copy pasting of the deserialization code involved if I were to do this for every struct, so I figured I should make a trait out of it:
use serde::{Deserialize, Deserializer};
use serde_json;
#[macro_use]
extern crate serde_derive;
trait Configurable {
type Config;
fn from_config(config: Self::Config) -> Self;
}
impl<'de, T, C> Deserialize<'de> for T
where
T: Configurable<Config = C>,
C: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
Self::Config::deserialize(deserializer).map(|config| Self::from_config(config))
}
}
struct Message {
text: String,
}
impl<'de> Configurable for Message {
type Config = MessageConfig;
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
However, the compiler is not happy about this:
error[E0119]: conflicting implementations of trait `_IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'_>` for type `std::boxed::Box<_>`:
--> src/lib.rs:11:1
|
11 | / impl<'de, T, C> Deserialize<'de> for T
12 | | where
13 | | T: Configurable<Config = C>,
14 | | C: Deserialize<'de>,
... |
21 | | }
22 | | }
| |_^
|
= note: conflicting implementation in crate `serde`:
- impl<'de, T> _IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'de> for std::boxed::Box<T>
where T: _IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'de>;
= note: downstream crates may implement trait `Configurable` for type `std::boxed::Box<_>`
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`)
--> src/lib.rs:11:1
|
11 | / impl<'de, T, C> Deserialize<'de> for T
12 | | where
13 | | T: Configurable<Config = C>,
14 | | C: Deserialize<'de>,
... |
21 | | }
22 | | }
| |_^ type parameter `T` must be used as the type parameter for some local type
|
= note: only traits defined in the current crate can be implemented for a type parameter
The error messages make little sense to me. What does Box
have to do with anything? And is it somehow possible to make this trait work?
rust deserialization traits serde
|
show 1 more comment
I am deserializing a YAML config file with Serde. For most structs I deserialize into, things are quite simple — there's a one-to-one relationship between the fields of the structs and the properties in my YAML file.
In a few cases, things are a bit more complicated. For these, the properties in the YAML file are better viewed as parameters to the constructor. The actual struct will have different fields, calculated from those.
For these cases, I have written separate config structs that I deserialize into. For simplicity, consider this silly example:
struct Message {
text: String,
}
impl Message {
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
To have Serde do the conversion from MessageConfig
to Message
for me, I implemented Deserialize
for Message
:
impl<'de> Deserialize<'de> for Message {
fn deserialize<D>(deserializer: D) -> Result<Message, D::Error>
where
D: Deserializer<'de>,
{
MessageConfig::deserialize(deserializer).map(|config| Message::from_config(config))
}
}
This works, but there would be a lot of copy pasting of the deserialization code involved if I were to do this for every struct, so I figured I should make a trait out of it:
use serde::{Deserialize, Deserializer};
use serde_json;
#[macro_use]
extern crate serde_derive;
trait Configurable {
type Config;
fn from_config(config: Self::Config) -> Self;
}
impl<'de, T, C> Deserialize<'de> for T
where
T: Configurable<Config = C>,
C: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
Self::Config::deserialize(deserializer).map(|config| Self::from_config(config))
}
}
struct Message {
text: String,
}
impl<'de> Configurable for Message {
type Config = MessageConfig;
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
However, the compiler is not happy about this:
error[E0119]: conflicting implementations of trait `_IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'_>` for type `std::boxed::Box<_>`:
--> src/lib.rs:11:1
|
11 | / impl<'de, T, C> Deserialize<'de> for T
12 | | where
13 | | T: Configurable<Config = C>,
14 | | C: Deserialize<'de>,
... |
21 | | }
22 | | }
| |_^
|
= note: conflicting implementation in crate `serde`:
- impl<'de, T> _IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'de> for std::boxed::Box<T>
where T: _IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'de>;
= note: downstream crates may implement trait `Configurable` for type `std::boxed::Box<_>`
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`)
--> src/lib.rs:11:1
|
11 | / impl<'de, T, C> Deserialize<'de> for T
12 | | where
13 | | T: Configurable<Config = C>,
14 | | C: Deserialize<'de>,
... |
21 | | }
22 | | }
| |_^ type parameter `T` must be used as the type parameter for some local type
|
= note: only traits defined in the current crate can be implemented for a type parameter
The error messages make little sense to me. What does Box
have to do with anything? And is it somehow possible to make this trait work?
rust deserialization traits serde
@Shepmaster I don't think it's a duplicate. The suggested dup: A struct has a field that references a trait. How do I deserialize? This question: I want to mass implement Deserialize for all types that implement a specific trait.
– Anders
Jan 2 at 22:38
If all of your structs have the same implementation for deserialization, then why not make them the same actual struct (or all contain the same struct) and implementDeserialize
for that? If they aren't the same, then what's the benefit of adding extra indirection?
– Shepmaster
Jan 2 at 22:56
The error is relatively straight-forward. Serde implementsDeserialize
forBox<T>
. Your implementation covers forT
, which might be aBox<T>
. A user of both crates could construct a type that could be deserialized as either your code or the one in Serde. This is disallowed.
– Shepmaster
Jan 2 at 23:01
@Shepmaster Maybe I am doing a basic mistake here. I thought by specifyingwhere T: Configurable<Config = C>
my implementation ofDeserialize
would only cover implementors ofConfigurable
.Box<T>
is not an implementor ofConfigurable
, so I figured there wouldn't be a problem.
– Anders
Jan 2 at 23:04
2
What prevents someone from implementingConfigurable
forBox<MyOwnType>
whenMyOwnType
also implementsDeserialize
?
– Shepmaster
Jan 2 at 23:06
|
show 1 more comment
I am deserializing a YAML config file with Serde. For most structs I deserialize into, things are quite simple — there's a one-to-one relationship between the fields of the structs and the properties in my YAML file.
In a few cases, things are a bit more complicated. For these, the properties in the YAML file are better viewed as parameters to the constructor. The actual struct will have different fields, calculated from those.
For these cases, I have written separate config structs that I deserialize into. For simplicity, consider this silly example:
struct Message {
text: String,
}
impl Message {
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
To have Serde do the conversion from MessageConfig
to Message
for me, I implemented Deserialize
for Message
:
impl<'de> Deserialize<'de> for Message {
fn deserialize<D>(deserializer: D) -> Result<Message, D::Error>
where
D: Deserializer<'de>,
{
MessageConfig::deserialize(deserializer).map(|config| Message::from_config(config))
}
}
This works, but there would be a lot of copy pasting of the deserialization code involved if I were to do this for every struct, so I figured I should make a trait out of it:
use serde::{Deserialize, Deserializer};
use serde_json;
#[macro_use]
extern crate serde_derive;
trait Configurable {
type Config;
fn from_config(config: Self::Config) -> Self;
}
impl<'de, T, C> Deserialize<'de> for T
where
T: Configurable<Config = C>,
C: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
Self::Config::deserialize(deserializer).map(|config| Self::from_config(config))
}
}
struct Message {
text: String,
}
impl<'de> Configurable for Message {
type Config = MessageConfig;
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
However, the compiler is not happy about this:
error[E0119]: conflicting implementations of trait `_IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'_>` for type `std::boxed::Box<_>`:
--> src/lib.rs:11:1
|
11 | / impl<'de, T, C> Deserialize<'de> for T
12 | | where
13 | | T: Configurable<Config = C>,
14 | | C: Deserialize<'de>,
... |
21 | | }
22 | | }
| |_^
|
= note: conflicting implementation in crate `serde`:
- impl<'de, T> _IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'de> for std::boxed::Box<T>
where T: _IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'de>;
= note: downstream crates may implement trait `Configurable` for type `std::boxed::Box<_>`
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`)
--> src/lib.rs:11:1
|
11 | / impl<'de, T, C> Deserialize<'de> for T
12 | | where
13 | | T: Configurable<Config = C>,
14 | | C: Deserialize<'de>,
... |
21 | | }
22 | | }
| |_^ type parameter `T` must be used as the type parameter for some local type
|
= note: only traits defined in the current crate can be implemented for a type parameter
The error messages make little sense to me. What does Box
have to do with anything? And is it somehow possible to make this trait work?
rust deserialization traits serde
I am deserializing a YAML config file with Serde. For most structs I deserialize into, things are quite simple — there's a one-to-one relationship between the fields of the structs and the properties in my YAML file.
In a few cases, things are a bit more complicated. For these, the properties in the YAML file are better viewed as parameters to the constructor. The actual struct will have different fields, calculated from those.
For these cases, I have written separate config structs that I deserialize into. For simplicity, consider this silly example:
struct Message {
text: String,
}
impl Message {
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
To have Serde do the conversion from MessageConfig
to Message
for me, I implemented Deserialize
for Message
:
impl<'de> Deserialize<'de> for Message {
fn deserialize<D>(deserializer: D) -> Result<Message, D::Error>
where
D: Deserializer<'de>,
{
MessageConfig::deserialize(deserializer).map(|config| Message::from_config(config))
}
}
This works, but there would be a lot of copy pasting of the deserialization code involved if I were to do this for every struct, so I figured I should make a trait out of it:
use serde::{Deserialize, Deserializer};
use serde_json;
#[macro_use]
extern crate serde_derive;
trait Configurable {
type Config;
fn from_config(config: Self::Config) -> Self;
}
impl<'de, T, C> Deserialize<'de> for T
where
T: Configurable<Config = C>,
C: Deserialize<'de>,
{
fn deserialize<D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
Self::Config::deserialize(deserializer).map(|config| Self::from_config(config))
}
}
struct Message {
text: String,
}
impl<'de> Configurable for Message {
type Config = MessageConfig;
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
However, the compiler is not happy about this:
error[E0119]: conflicting implementations of trait `_IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'_>` for type `std::boxed::Box<_>`:
--> src/lib.rs:11:1
|
11 | / impl<'de, T, C> Deserialize<'de> for T
12 | | where
13 | | T: Configurable<Config = C>,
14 | | C: Deserialize<'de>,
... |
21 | | }
22 | | }
| |_^
|
= note: conflicting implementation in crate `serde`:
- impl<'de, T> _IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'de> for std::boxed::Box<T>
where T: _IMPL_DESERIALIZE_FOR_MessageConfig::_serde::Deserialize<'de>;
= note: downstream crates may implement trait `Configurable` for type `std::boxed::Box<_>`
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`)
--> src/lib.rs:11:1
|
11 | / impl<'de, T, C> Deserialize<'de> for T
12 | | where
13 | | T: Configurable<Config = C>,
14 | | C: Deserialize<'de>,
... |
21 | | }
22 | | }
| |_^ type parameter `T` must be used as the type parameter for some local type
|
= note: only traits defined in the current crate can be implemented for a type parameter
The error messages make little sense to me. What does Box
have to do with anything? And is it somehow possible to make this trait work?
rust deserialization traits serde
rust deserialization traits serde
edited Jan 2 at 22:53
Shepmaster
160k16330473
160k16330473
asked Jan 2 at 21:00
AndersAnders
5,82963255
5,82963255
@Shepmaster I don't think it's a duplicate. The suggested dup: A struct has a field that references a trait. How do I deserialize? This question: I want to mass implement Deserialize for all types that implement a specific trait.
– Anders
Jan 2 at 22:38
If all of your structs have the same implementation for deserialization, then why not make them the same actual struct (or all contain the same struct) and implementDeserialize
for that? If they aren't the same, then what's the benefit of adding extra indirection?
– Shepmaster
Jan 2 at 22:56
The error is relatively straight-forward. Serde implementsDeserialize
forBox<T>
. Your implementation covers forT
, which might be aBox<T>
. A user of both crates could construct a type that could be deserialized as either your code or the one in Serde. This is disallowed.
– Shepmaster
Jan 2 at 23:01
@Shepmaster Maybe I am doing a basic mistake here. I thought by specifyingwhere T: Configurable<Config = C>
my implementation ofDeserialize
would only cover implementors ofConfigurable
.Box<T>
is not an implementor ofConfigurable
, so I figured there wouldn't be a problem.
– Anders
Jan 2 at 23:04
2
What prevents someone from implementingConfigurable
forBox<MyOwnType>
whenMyOwnType
also implementsDeserialize
?
– Shepmaster
Jan 2 at 23:06
|
show 1 more comment
@Shepmaster I don't think it's a duplicate. The suggested dup: A struct has a field that references a trait. How do I deserialize? This question: I want to mass implement Deserialize for all types that implement a specific trait.
– Anders
Jan 2 at 22:38
If all of your structs have the same implementation for deserialization, then why not make them the same actual struct (or all contain the same struct) and implementDeserialize
for that? If they aren't the same, then what's the benefit of adding extra indirection?
– Shepmaster
Jan 2 at 22:56
The error is relatively straight-forward. Serde implementsDeserialize
forBox<T>
. Your implementation covers forT
, which might be aBox<T>
. A user of both crates could construct a type that could be deserialized as either your code or the one in Serde. This is disallowed.
– Shepmaster
Jan 2 at 23:01
@Shepmaster Maybe I am doing a basic mistake here. I thought by specifyingwhere T: Configurable<Config = C>
my implementation ofDeserialize
would only cover implementors ofConfigurable
.Box<T>
is not an implementor ofConfigurable
, so I figured there wouldn't be a problem.
– Anders
Jan 2 at 23:04
2
What prevents someone from implementingConfigurable
forBox<MyOwnType>
whenMyOwnType
also implementsDeserialize
?
– Shepmaster
Jan 2 at 23:06
@Shepmaster I don't think it's a duplicate. The suggested dup: A struct has a field that references a trait. How do I deserialize? This question: I want to mass implement Deserialize for all types that implement a specific trait.
– Anders
Jan 2 at 22:38
@Shepmaster I don't think it's a duplicate. The suggested dup: A struct has a field that references a trait. How do I deserialize? This question: I want to mass implement Deserialize for all types that implement a specific trait.
– Anders
Jan 2 at 22:38
If all of your structs have the same implementation for deserialization, then why not make them the same actual struct (or all contain the same struct) and implement
Deserialize
for that? If they aren't the same, then what's the benefit of adding extra indirection?– Shepmaster
Jan 2 at 22:56
If all of your structs have the same implementation for deserialization, then why not make them the same actual struct (or all contain the same struct) and implement
Deserialize
for that? If they aren't the same, then what's the benefit of adding extra indirection?– Shepmaster
Jan 2 at 22:56
The error is relatively straight-forward. Serde implements
Deserialize
for Box<T>
. Your implementation covers for T
, which might be a Box<T>
. A user of both crates could construct a type that could be deserialized as either your code or the one in Serde. This is disallowed.– Shepmaster
Jan 2 at 23:01
The error is relatively straight-forward. Serde implements
Deserialize
for Box<T>
. Your implementation covers for T
, which might be a Box<T>
. A user of both crates could construct a type that could be deserialized as either your code or the one in Serde. This is disallowed.– Shepmaster
Jan 2 at 23:01
@Shepmaster Maybe I am doing a basic mistake here. I thought by specifying
where T: Configurable<Config = C>
my implementation of Deserialize
would only cover implementors of Configurable
. Box<T>
is not an implementor of Configurable
, so I figured there wouldn't be a problem.– Anders
Jan 2 at 23:04
@Shepmaster Maybe I am doing a basic mistake here. I thought by specifying
where T: Configurable<Config = C>
my implementation of Deserialize
would only cover implementors of Configurable
. Box<T>
is not an implementor of Configurable
, so I figured there wouldn't be a problem.– Anders
Jan 2 at 23:04
2
2
What prevents someone from implementing
Configurable
for Box<MyOwnType>
when MyOwnType
also implements Deserialize
?– Shepmaster
Jan 2 at 23:06
What prevents someone from implementing
Configurable
for Box<MyOwnType>
when MyOwnType
also implements Deserialize
?– Shepmaster
Jan 2 at 23:06
|
show 1 more comment
1 Answer
1
active
oldest
votes
I am not sure if there's a way to define such a broad trait without causing conflicting implementations. What you could do is use a macro to avoid repetition:
use serde::{Deserialize, Deserializer};
use serde_json;
use serde_json::Error;
#[macro_use]
extern crate serde_derive;
struct Message {
text: String,
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
impl Message {
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
macro_rules! derive_configurable_serializer {
( $t:ident, $c:ident ) => {
impl<'de> Deserialize<'de> for $t {
fn deserialize<D>(deserializer: D) -> Result<$t, D::Error>
where
D: Deserializer<'de>,
{
$c::deserialize(deserializer).map(|config| $t::from_config(config))
}
}
};
}
derive_configurable_serializer!(Message, MessageConfig);
fn main() -> Result<(), Error> {
let data = r#"{ "first_half": "John", "second_half": "Doe" }"#;
let p: Message = serde_json::from_str(data)?;
println!("Hello, {}!", p.text);
Ok(())
}
Nice idea with a macro, and thanks for showing how to do it! I don't understand what's up with the conflicting implementations, though. There is no type that implements both Configurable and Deserialize from some other source.
– Anders
Jan 2 at 22:42
1
@Anders honestly - I don't know. I tried finding a solution with the trait, got various error messages. Maybe someone with a deeper understanding of Rust traits can explain how to do it / why it isn't possible.
– Ramon Snir
Jan 2 at 22:44
add a comment |
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "1"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: true,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: 10,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54013125%2fhow-can-i-mass-implement-deserialize-for-all-types-that-implement-a-specific-tra%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
I am not sure if there's a way to define such a broad trait without causing conflicting implementations. What you could do is use a macro to avoid repetition:
use serde::{Deserialize, Deserializer};
use serde_json;
use serde_json::Error;
#[macro_use]
extern crate serde_derive;
struct Message {
text: String,
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
impl Message {
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
macro_rules! derive_configurable_serializer {
( $t:ident, $c:ident ) => {
impl<'de> Deserialize<'de> for $t {
fn deserialize<D>(deserializer: D) -> Result<$t, D::Error>
where
D: Deserializer<'de>,
{
$c::deserialize(deserializer).map(|config| $t::from_config(config))
}
}
};
}
derive_configurable_serializer!(Message, MessageConfig);
fn main() -> Result<(), Error> {
let data = r#"{ "first_half": "John", "second_half": "Doe" }"#;
let p: Message = serde_json::from_str(data)?;
println!("Hello, {}!", p.text);
Ok(())
}
Nice idea with a macro, and thanks for showing how to do it! I don't understand what's up with the conflicting implementations, though. There is no type that implements both Configurable and Deserialize from some other source.
– Anders
Jan 2 at 22:42
1
@Anders honestly - I don't know. I tried finding a solution with the trait, got various error messages. Maybe someone with a deeper understanding of Rust traits can explain how to do it / why it isn't possible.
– Ramon Snir
Jan 2 at 22:44
add a comment |
I am not sure if there's a way to define such a broad trait without causing conflicting implementations. What you could do is use a macro to avoid repetition:
use serde::{Deserialize, Deserializer};
use serde_json;
use serde_json::Error;
#[macro_use]
extern crate serde_derive;
struct Message {
text: String,
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
impl Message {
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
macro_rules! derive_configurable_serializer {
( $t:ident, $c:ident ) => {
impl<'de> Deserialize<'de> for $t {
fn deserialize<D>(deserializer: D) -> Result<$t, D::Error>
where
D: Deserializer<'de>,
{
$c::deserialize(deserializer).map(|config| $t::from_config(config))
}
}
};
}
derive_configurable_serializer!(Message, MessageConfig);
fn main() -> Result<(), Error> {
let data = r#"{ "first_half": "John", "second_half": "Doe" }"#;
let p: Message = serde_json::from_str(data)?;
println!("Hello, {}!", p.text);
Ok(())
}
Nice idea with a macro, and thanks for showing how to do it! I don't understand what's up with the conflicting implementations, though. There is no type that implements both Configurable and Deserialize from some other source.
– Anders
Jan 2 at 22:42
1
@Anders honestly - I don't know. I tried finding a solution with the trait, got various error messages. Maybe someone with a deeper understanding of Rust traits can explain how to do it / why it isn't possible.
– Ramon Snir
Jan 2 at 22:44
add a comment |
I am not sure if there's a way to define such a broad trait without causing conflicting implementations. What you could do is use a macro to avoid repetition:
use serde::{Deserialize, Deserializer};
use serde_json;
use serde_json::Error;
#[macro_use]
extern crate serde_derive;
struct Message {
text: String,
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
impl Message {
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
macro_rules! derive_configurable_serializer {
( $t:ident, $c:ident ) => {
impl<'de> Deserialize<'de> for $t {
fn deserialize<D>(deserializer: D) -> Result<$t, D::Error>
where
D: Deserializer<'de>,
{
$c::deserialize(deserializer).map(|config| $t::from_config(config))
}
}
};
}
derive_configurable_serializer!(Message, MessageConfig);
fn main() -> Result<(), Error> {
let data = r#"{ "first_half": "John", "second_half": "Doe" }"#;
let p: Message = serde_json::from_str(data)?;
println!("Hello, {}!", p.text);
Ok(())
}
I am not sure if there's a way to define such a broad trait without causing conflicting implementations. What you could do is use a macro to avoid repetition:
use serde::{Deserialize, Deserializer};
use serde_json;
use serde_json::Error;
#[macro_use]
extern crate serde_derive;
struct Message {
text: String,
}
#[derive(Deserialize)]
struct MessageConfig {
first_half: String,
second_half: String,
}
impl Message {
fn from_config(config: MessageConfig) -> Message {
Message {
text: format!("{} {}", config.first_half, config.second_half),
}
}
}
macro_rules! derive_configurable_serializer {
( $t:ident, $c:ident ) => {
impl<'de> Deserialize<'de> for $t {
fn deserialize<D>(deserializer: D) -> Result<$t, D::Error>
where
D: Deserializer<'de>,
{
$c::deserialize(deserializer).map(|config| $t::from_config(config))
}
}
};
}
derive_configurable_serializer!(Message, MessageConfig);
fn main() -> Result<(), Error> {
let data = r#"{ "first_half": "John", "second_half": "Doe" }"#;
let p: Message = serde_json::from_str(data)?;
println!("Hello, {}!", p.text);
Ok(())
}
edited Jan 2 at 22:54
Shepmaster
160k16330473
160k16330473
answered Jan 2 at 22:20
Ramon SnirRamon Snir
6,24033356
6,24033356
Nice idea with a macro, and thanks for showing how to do it! I don't understand what's up with the conflicting implementations, though. There is no type that implements both Configurable and Deserialize from some other source.
– Anders
Jan 2 at 22:42
1
@Anders honestly - I don't know. I tried finding a solution with the trait, got various error messages. Maybe someone with a deeper understanding of Rust traits can explain how to do it / why it isn't possible.
– Ramon Snir
Jan 2 at 22:44
add a comment |
Nice idea with a macro, and thanks for showing how to do it! I don't understand what's up with the conflicting implementations, though. There is no type that implements both Configurable and Deserialize from some other source.
– Anders
Jan 2 at 22:42
1
@Anders honestly - I don't know. I tried finding a solution with the trait, got various error messages. Maybe someone with a deeper understanding of Rust traits can explain how to do it / why it isn't possible.
– Ramon Snir
Jan 2 at 22:44
Nice idea with a macro, and thanks for showing how to do it! I don't understand what's up with the conflicting implementations, though. There is no type that implements both Configurable and Deserialize from some other source.
– Anders
Jan 2 at 22:42
Nice idea with a macro, and thanks for showing how to do it! I don't understand what's up with the conflicting implementations, though. There is no type that implements both Configurable and Deserialize from some other source.
– Anders
Jan 2 at 22:42
1
1
@Anders honestly - I don't know. I tried finding a solution with the trait, got various error messages. Maybe someone with a deeper understanding of Rust traits can explain how to do it / why it isn't possible.
– Ramon Snir
Jan 2 at 22:44
@Anders honestly - I don't know. I tried finding a solution with the trait, got various error messages. Maybe someone with a deeper understanding of Rust traits can explain how to do it / why it isn't possible.
– Ramon Snir
Jan 2 at 22:44
add a comment |
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54013125%2fhow-can-i-mass-implement-deserialize-for-all-types-that-implement-a-specific-tra%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
@Shepmaster I don't think it's a duplicate. The suggested dup: A struct has a field that references a trait. How do I deserialize? This question: I want to mass implement Deserialize for all types that implement a specific trait.
– Anders
Jan 2 at 22:38
If all of your structs have the same implementation for deserialization, then why not make them the same actual struct (or all contain the same struct) and implement
Deserialize
for that? If they aren't the same, then what's the benefit of adding extra indirection?– Shepmaster
Jan 2 at 22:56
The error is relatively straight-forward. Serde implements
Deserialize
forBox<T>
. Your implementation covers forT
, which might be aBox<T>
. A user of both crates could construct a type that could be deserialized as either your code or the one in Serde. This is disallowed.– Shepmaster
Jan 2 at 23:01
@Shepmaster Maybe I am doing a basic mistake here. I thought by specifying
where T: Configurable<Config = C>
my implementation ofDeserialize
would only cover implementors ofConfigurable
.Box<T>
is not an implementor ofConfigurable
, so I figured there wouldn't be a problem.– Anders
Jan 2 at 23:04
2
What prevents someone from implementing
Configurable
forBox<MyOwnType>
whenMyOwnType
also implementsDeserialize
?– Shepmaster
Jan 2 at 23:06