-
-
Notifications
You must be signed in to change notification settings - Fork 794
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
Rc<> gets deserialized incorrectly #194
Comments
IMO this is kind of a big deal. Any word on progress towards dealing with this? |
I agree. I think serde either needs to drop Rc support or add an annotation that users can specify (or something so people are aware of this issue). It's pretty hard to come up with a general solution that doesn't have other issues. In any case, this is a big backwards compatibility hazard because this will require breaking changes to fix. |
I am open to dropping Serialize and Deserialize impls for Rc in the next breaking release, and requiring explicit serialize_with and deserialize_with to opt into serializing them. |
Whoa, I personally find My application will suffer from removal of these implementations. I use serde for deserializing plain-old-data configs with On the other hand, one can always write his own personal fancy deserializer if he wants interning. |
@alexbool: the reverse argument is that you could use Maybe we should provide a |
@oli-obk while this is clearly possible, I still think this is not worth breaking projects |
@alexbool It's not a silent breaking change, so everyone will notice and it will be in a major version bump which will break projects most likely anyway. The reverse is a silent source of bugs. The advantage of breaking it is that in the future we can re-add it when/if we figure out a way to serialize/deserialize the duplication. |
@oli-obk what do you call "not silent"? Do we have a deprecation that is a compilation warning? At the moment I doubt so. |
@alexbool The following will stop compiling, because you can only have fields that implement #[derive(Deserialize, Serialize)]
struct Foo {
a: i32,
b: Arc<String>, //~ERROR Arc<String> does not implement Deserialize
} |
@oli-obk In my understanding, this is simply a breaking change, a silent one. No one is expecting that until we can throw a compilation warning some time before the actual breakage will occur. |
a silent breaking change is a change that only occurs at runtime
When you do a major version bump, you should always expect breakage. |
Can we throw a deprcation message during proc-macro expansion? |
I think there's a disconnect what "silent" means. The current behaviour is a bug in my book, because it means a serialize/deserialize cycle breaks programs "silently" - it appears to work, but doesn't. A breaking change that breaks the compile is not silent, independent of whether a deprecation note happened earlier or not. Under semver, when you update to a version with breaking changes, you are supposed to read the release notes to learn whether you're affected, then decide whether you want to upgrade and if you decide you want to you deal with the breakage. That seems normal and expected? |
I'm -1 on removing the impls. Why would one assume that serialising and then deserialising a |
None would assume that, but you might accidentally end up creating code that expects that it would. Generic structures which derive Serialize and Deserialize might get inialized with Rc.... Also, in the few cases you actually want cloning serialization, you can use serialize_with. |
I put together a proof of concept of an efficient Rc<> DAG serialization/deserialization approach here in case anyone is interested in pursuing this further: see bincode-org/bincode#139 (comment). |
One idea I had for this was that we could add an API to This does have some smell to it though. |
@erickt take a look at the implementation in bincode-org/bincode#139 (comment) (the hashmap could be swapped out for a typemap). I think this should be a separate library on top of Serde. It does not need to be baked into the Deserializer API. |
I am still torn on this. I understand the concern that implicitly duplicating Rc data may be unexpected and is not aligned with Rust's tendency to make expensive operations explicit. I also know there are use cases where serializing an Rc the current way is exactly what is desired and working around a missing impl would be obnoxious. @shahn @dylanede or others, does anyone have an example of a library using Rc and Serde where serializing copies of an Rc would lead to undesirable behavior? Perhaps putting Rc impls in Serde behind a cfg would be an acceptable middle ground? |
What about |
I don't have an example for a public crate, but this ticket exists because I was using serde to serialize the state of a toy compiler and deserializing broke it completely (I was using pointer equality to check for equality of two objects behind an Rc, nested into a much larger struct). Since Serde allowed me to just derive Serialize, I assumed it would work correctly. |
This is not the default behaviour of |
I don't think the point you're making is correct. I did not modify the way Rc checks equality at all (I could not do that in fact, because of coherence). Rc works for any T, completely independently of whether T's PartialEq impl is derived, missing, or custom-defined. |
Yes definitely. Putting this behind a cfg transports the message that this might be what you want, but it might also break your assumptions. I think that's the right tradeoff. Plus it's a very easily fixed breaking change, which I hope won't be too hard for people to adapt to. |
Cool. I am moving forward with the cfg and closing this issue but we have some time before this will be released, so I am open to continuing the discussion here if other people have strong opinions. |
@dtolnay Question. Given how cargo deduplicates dependencies, couldn't one accidentally opt into this feature by having a dependency that, as its implementation detail, requests the rc feature from serde? |
That is correct. This is just a way to improve the chance that people understand what they are doing. |
hm, that would be sad if a popular dependency used that feature. Are there better alternative ideas? [edit]Ah. serde 1.0 is out, so this is probably it for now :) |
Yeah it shipped so it's too late. I don't understand why you think it would be sad, the existence of this feature is what is sad. :) |
As I see it, enabling the feature by default is not a breaking change, so the feature is the most sensible choice in the presence of uncertainty. If you want to make sure you don't accidentally end up with a derived impl containing an rc, open a clippy issue. |
Add a trait for floating-point constants The pull request is to address issue serde-rs#194. In order to keep the library organized, I’ve introduced a new trait for the new functionality. The trait is supposed to closely follows the [`consts`](https://doc.rust-lang.org/std/f64/consts/index.html) module from the standard library. There are at least the following three open questions: 1. What should the name of the trait be? Currently, it’s `Constant`; an alternative is `Consts`. 2. What should the names of the getters be? Currently, they are lower-case versions of the constants defined in the `consts` module. Note that `Float` provides `log2` and `log10`, whereas `LOG_2` and `LOG_10` get translated into `log_2` and `log_10`, respectively. 3. Should `Float` require the new trait? Or should it be left up to the user? Please let me know what you think. Thank you! Regards, Ivan
The way serde currently treats ref-counted data structures means that it creates copies of the references objects. This is bad for several reasons:
and maybe more that I can't think of right away. I think there should at least be big warnings in the documentation about this behaviour, but the support should be either fixed or deprecated, imo.
The text was updated successfully, but these errors were encountered: