Skip to content
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

The serde crate's derive feature interacts poorly with 2018-style macro imports #1441

Closed
sfackler opened this issue Dec 8, 2018 · 6 comments
Labels

Comments

@sfackler
Copy link
Contributor

sfackler commented Dec 8, 2018

Say I have a crate that derives Serialize for one type, and has to manually implement it for another. I believe the "standard" way of doing this would be

use serde_derive::Serialize;
use serde::{Serialize, Serializer};

#[derive(Serialize)]
struct Foo(u32);

struct Bar(u32);

impl Serialize for Bar {
    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        self.0.to_string().serialize(s)
    }
}

This works fine in isolation, but if this crate is used alongside some other crate that activates serde's derive feature, it will stop compiling:

error[E0252]: the name `Serialize` is defined multiple times                                                                                                            
 --> src/lib.rs:2:13                                                                                                                                                    
  |                                                                                                                                                                     
1 | use serde_derive::Serialize;                                                                                                                                        
  |     ----------------------- previous import of the macro `Serialize` here                                                                                           
2 | use serde::{Serialize, Serializer};                                                                                                                                 
  |             ^^^^^^^^^ `Serialize` reimported here                                                                                                                   
  |                                                                                                                                                                     
  = note: `Serialize` must be defined only once in the macro namespace of this module                                                                                   
help: you can use `as` to change the binding name of the import                                                                                                         
  |                                                                                                                                                                     
2 | use serde::{Serialize as OtherSerialize, Serializer};                                                                                                               
  |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^                                                                                                                             
                                                                                                                                                                        
error: aborting due to previous error                                                                                                                                   
                                                                                                                                                                        
For more information about this error, try `rustc --explain E0252`. 

The crate can be made to compile in both scenarios by importing Serialize and Serializer from serde::ser rather than serde, but it seems like an easy thing to forget, and you're not going to notice until it breaks someone downstream that's trying to use your crate.

What do you think the right way of avoiding this is? Should the recommended approach change from depending on serde_derive to using the derive feature directly?

@dtolnay dtolnay added the docs label Dec 8, 2018
@dtolnay
Copy link
Member

dtolnay commented Dec 8, 2018

I am on board with your proposed solution -- let's update all documentation and examples to recommend depending on the derive macros re-exported by serde rather than using serde_derive.

@hcpl
Copy link
Contributor

hcpl commented Dec 9, 2018

This issue made me think about the impact path-imported macros has on re-exported macros and other items with the exact same name. Previously, they were in completely separate namespaces, so that was fine. Now re-exporting macros will silently re-assign a new item under that name (you could say now the macro namespace silently leaks into the other items namespace).

Which means the pattern where:

  • there are related proc-macro and "other items defining" crates;
  • the latter re-exports items from the former;
  • the proc-macro crate is not disallowed/banned for usage by the author(s);

does not seamlessly work anymore in all newer Rust versions because of the edge case this issue presents.

As a result, now that using the serde_derive is dangerous in upstream crates, should it be deprecated? This will surely create a huge impact on the whole serde ecosystem, but prioritizing buildability is the way to go, in my opinion.

Deprecating will have an added benefit of being future-compatible with procedural macros that are defined in the same crate as non-procedural macro items.

If deprecating serde_derive right now is not viable, we could fix equivalent issues in major crates that use Serde in the beginning. This will raise awareness about this particular pattern with proc-macro crates and more crates will get updated to not follow it. And only when it feels OK to do so, deprecate.

@dtolnay
Copy link
Member

dtolnay commented Dec 9, 2018

Instead of deprecating serde_derive I would prefer if multiple imports of the same item did not conflict with each other. In this case serde::Serialize (the macro) and serde_derive::Serialize resolve to the same item, after re-exports are considered. It should be fine for the compiler to accept having both of those imports because together they only import a single macro -- thus invocations of that macro can be resolved without an ambiguity. Will see if I can find an existing issue for this.

@hcpl
Copy link
Contributor

hcpl commented Dec 9, 2018

To be fair, use serde::Serialize; meaning:

  • Serialize trait when derive feature is disabled;
  • Serialize trait and Serialize macro when derive feature is enabled;

is quite surprising without knowing the context, both because a single use can import 2 items at once and because a use can add an item of a different type to the import list depending on a #[cfg] --- with non-macros the compiler would complain. But macros were always special in one way or another. so shrug.

@vorner
Copy link

vorner commented Jan 12, 2019

I find the thing surprising in the opposite direction too, somehow. I usually enable the derive feature instead of depending separately on serde-derive (that seems more comfortable). Anyway, if I import as use serde::Deserialize, I get the macro, but if I do use serde::de::{Deserialize, Deserializer};, the macro is no longer available.

saks added a commit to saks/aws-lambda-rust-runtime that referenced this issue Jan 28, 2019
saks added a commit to saks/aws-lambda-rust-runtime that referenced this issue Jan 28, 2019
davidbarsky pushed a commit to awslabs/aws-lambda-rust-runtime that referenced this issue Jan 28, 2019
@dtolnay
Copy link
Member

dtolnay commented Feb 9, 2019

I updated all example code to use serde's derive feature rather than depending directly on serde_derive.

@dtolnay dtolnay closed this as completed Feb 9, 2019
veeg pushed a commit to veeg/shiplift that referenced this issue Feb 22, 2019
This is in line with best practices recommended
by serde[1]. This will also resolve downstream
crates depending on shiplift who enable the
serde derive feature, due to Cargos unification
of features for each crate[2].

[1]: serde-rs/serde#1441
[2]: rust-lang/cargo#4361 (comment)
softprops pushed a commit to softprops/shiplift that referenced this issue Feb 22, 2019
This is in line with best practices recommended
by serde[1]. This will also resolve downstream
crates depending on shiplift who enable the
serde derive feature, due to Cargos unification
of features for each crate[2].

[1]: serde-rs/serde#1441
[2]: rust-lang/cargo#4361 (comment)
wngr added a commit to wngr/rogcat that referenced this issue Dec 12, 2019
`serde_derive`. Works around the issue, when using `rogcat` as
a library; see serde-rs/serde#1441
wngr added a commit to wngr/rogcat that referenced this issue Dec 12, 2019
`serde_derive`. Works around the issue, when using `rogcat` as
a library; see serde-rs/serde#1441
flxo pushed a commit to flxo/rogcat that referenced this issue Dec 13, 2019
`serde_derive`. Works around the issue, when using `rogcat` as
a library; see serde-rs/serde#1441
ir210 pushed a commit to ir210/rogcat that referenced this issue Dec 16, 2019
`serde_derive`. Works around the issue, when using `rogcat` as
a library; see serde-rs/serde#1441
flxo pushed a commit to flxo/rogcat that referenced this issue Dec 16, 2019
* Move parsers to the library

* use serde `derive` feauture to replace explicit dependency on
`serde_derive`. Works around the issue, when using `rogcat` as
a library; see serde-rs/serde#1441
JohnTitor pushed a commit to actix/examples that referenced this issue Jan 12, 2020
rofrol added a commit to rofrol/actix-examples that referenced this issue Jan 12, 2020
vsrinivas pushed a commit to vsrinivas/fuchsia that referenced this issue Mar 25, 2020
This change:
- Allows for future imports of crates that dep on the serde derive
  feature (e.g. trybuild).
- Migrates all existing dual {serde, serde_derive} deps to use
  serde only. The crate migration per-se is not necessary, but without
  changes the build breaks due to an issue with the macros being
  imported multiple times. Read more @
  serde-rs/serde#1441

Change-Id: I02c5dc59aec77b30898684a9799a4541c3ed80a5
avalent pushed a commit to avalent/qapi-rs that referenced this issue Jul 24, 2020
Needed to fix compilation issue encountered when qapi was used together
with another crate that activated serde's derive feature. Followed the
recommendation here: serde-rs/serde#1441
ephemeralriggs pushed a commit to google/omaha-client that referenced this issue Feb 19, 2024
This change:
- Allows for future imports of crates that dep on the serde derive
  feature (e.g. trybuild).
- Migrates all existing dual {serde, serde_derive} deps to use
  serde only. The crate migration per-se is not necessary, but without
  changes the build breaks due to an issue with the macros being
  imported multiple times. Read more @
  serde-rs/serde#1441

Change-Id: I02c5dc59aec77b30898684a9799a4541c3ed80a5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Development

No branches or pull requests

4 participants