34

I'm trying to use Diesel to query a MySQL database and display the results with a Handlebars template with Rocket.

I have this in models.rs

#[derive(Queryable, Serialize)]
pub struct Post {
    pub id: i32,
    pub title: String,
    pub text: String,
    pub published: bool,
}

cargo run outputs this:

  --> src/main.rs:69:5
   |
69 |     Template::render("index", &results)
   |     ^^^^^^^^^^^^^^^^ the trait `serde::ser::Serialize` is not implemented for `tasty::models::Post`
   |
   = note: required because of the requirements on the impl of `serde::ser::Serialize` for `std::vec::Vec<tasty::models::Post>`
   = note: required by `rocket_contrib::Template::render`

In my Cargo.toml, I have this:

[dependencies]
chrono = "0.3.0"
rocket = "0.2.8"
rocket_codegen = "0.2.8"
serde = "1.0.8"
serde_derive = "1.0.8"
serde_json = "1.0.2"
mysql = "11.1.2"
diesel = { version = "0.13.0", features = ["mysql","chrono"] }
diesel_codegen = { version = "0.13.0", features = ["mysql"] }
dotenv = "0.10.0"

[dependencies.rocket_contrib]
version = "*"
default-features = false
features = ["handlebars_templates"]

I have read that Diesel does not support Serialize at the moment, but I am not sure.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
haheute
  • 2,129
  • 3
  • 32
  • 49
  • 1
    *Questions seeking debugging help ("why isn't this code working?") must include the desired behavior, a specific problem or error and **the shortest code necessary to reproduce it in the question itself**.* — your error message points to code that you haven't shown, therefore we are less likely to know what the problem is and thus we are forced to guess, which wastes your time and ours. – Shepmaster Jun 08 '17 at 13:34

1 Answers1

44

The general problem is that the code has multiple versions of the crate, each providing a different version of the traits. The fact that Rust allows this is a good thing, but the error messages around it are confusing.

Your crate implements Serialize from version A but the library is using version B in a public interface. These traits are not compatible, so when you pass your type implementing Serialize@A to the function requiring Serialize@B, the compiler stops you.

While your example is about different traits, it's also possible for this to occur for types which have been re-exported from a crate.

cargo tree, available starting in Rust 1.44 is highly useful to verify this is your problem. It shows all of your dependencies and their versions. It even has a -d flag to show duplicate dependencies! That mode isn't shown here, but is highly useful.

The general solution is to manually restrict your version of Serde in your Cargo.toml to match the rest of the dependencies:

serde = "0.9"
serde_derive = "0.9"
serde_json = "0.9"

This may not always be possible, in which case you may need to hound the crate owners to upgrade their dependencies.


Worked examples

Rocket

[dependencies]
chrono = "0.3.0"
rocket = "0.2.8"
rocket_codegen = "0.2.8"
serde = "1.0.8"
serde_derive = "1.0.8"
serde_json = "1.0.2"
mysql = "11.1.2"
diesel = { version = "0.13.0", features = ["mysql","chrono"] }
diesel_codegen = { version = "0.13.0", features = ["mysql"] }
dotenv = "0.10.0"

[dependencies.rocket_contrib]
version = "*"
default-features = false
features = ["handlebars_templates"]

rocket_contrib 0.2.8 depends on Serde 0.9, but you have pulled in Serde 1.0. This abridged snippet from cargo tree shows the problem:

reproduction v0.1.0 (file:///private/tmp/reproduction)
├── rocket_contrib v0.2.8
│   ├── handlebars v0.25.3
│   │   └── serde_json v0.9.10
│   │       └── serde v0.9.15
│   ├── serde v0.9.15 (*)
│   └── serde_json v0.9.10 (*)
├── serde v1.0.8 (*)
├── serde_derive v1.0.8
│   ├── serde_derive_internals v0.15.1
└── serde_json v1.0.2 (*)

The upcoming version 0.3 of Rocket should allow using Serde 1.0.

Iron / Bson / MongoDB

[dependencies]
bodyparser = "0.5"
bson = "0.8"
iron = "0.5"
jwt = "0.4"
mongodb = "0.3"
router = "0.5"
rust-crypto = "0.2"
serde = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
time = "0.1"

bodyparser 0.5 depends on Serde 0.8, MongoDB has pulled in 0.9, but the crate and BSON have pulled in Serde 1.0. This abridged snippet from cargo tree shows the problem:

reproduction v0.1.0 (file:///private/tmp/reproduction)
├── bodyparser v0.5.0
│   ├── serde v0.8.23
│   └── serde_json v0.8.6
│       └── serde v0.8.23 (*)
├── bson v0.8.0
│   ├── serde v1.0.8
│   ├── serde_json v1.0.2
│   │   └── serde v1.0.8 (*)
├── mongodb v0.3.0
│   ├── textnonce v0.6.0
│   │   ├── serde v0.9.15
│   │   └── serde_derive v0.9.15
├── serde v1.0.8 (*)
├── serde_derive v1.0.8
├── serde_json v1.0.2 (*)

Bodyparser 0.7.0 should support Serde 1.0. The state of textnonce is less clear, but that dependency might be a private one, so it might not matter in this case.

Diesel / Chrono

chrono = "0.4.0"
diesel = { version = "0.13.0", features = [ "chrono", "sqlite" ] }
diesel_codegen = { version = "0.13.0", features = [ "sqlite" ] }
dotenv = "0.9.0"

The current version of Chrono is 0.4.0, but Diesel only knows how to serialize Chrono 0.3.0.

reproduction v0.1.0 (file:///private/tmp/reproduction)
├── chrono v0.4.0
├── diesel v0.13.0
│   ├── chrono v0.3.0

blowfish / block-cipher-trait

[dependencies]
blowfish = "0.2.1"
block-cipher-trait = "0.3.0"
reproduction v0.1.0 (file:///private/tmp/reproduction)
├── block-cipher-trait v0.3.0
│── blowfish v0.2.1
    ├── block-cipher-trait v0.2.0

conrod / piston2d-graphics

[dependencies]
piston_window = "0.74.0"
conrod = { version = "0.56.0", features = ["piston"] }
repro v0.1.0 (file:///private/tmp/repro)
├── conrod v0.56.0
│   ├── piston2d-graphics v0.23.0
└── piston_window v0.74.0
    ├── piston2d-graphics v0.24.0 (*)

actix / futures

[dependencies]
actix-web = "0.6.10"
futures = "0.2.1"
repro v0.1.0 (file:///private/tmp/repro)
├── actix-web v0.6.12
│   ├── actix v0.5.8
│   │   ├── futures v0.1.21
└── futures v0.2.1

A bright future?

RFC 1977 proposes to introduce the notion of public and private dependencies to Cargo. If you use a crate that in turn publicly exposes another crate's types, Cargo would ensure that you use a single unified version for the crate with the common types.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • This answer is great, and cargo tree helped me find my problem. I went to read the cited RFC 1977 but I can't see how that solves the problem. In my case I was lucky that the dependency change was minor, so I was able to clone the github repository of the conflicting project and update the dependency in my own fork. This is clearly not a solution that will satisfy most cases. Since this answer is from 2017, I wonder what the current state of affairs is regarding this issue. – palako Dec 23 '20 at 02:42
  • @palako The answer was [last edited 17 days ago](https://stackoverflow.com/posts/44437441/revisions); I keep an eye on popular topics. – Shepmaster Dec 23 '20 at 02:56
  • @palako public/private dependencies will help because Cargo will know that it is able to pick arbitrary versions inside of a private cluster of crates, but can ensure that a *single unified version* is used for a public dependency. – Shepmaster Dec 23 '20 at 02:59
  • Thanks Shepmaster. But public/private would be something that depends on the crates writers, no? If I am using two libraries in my code that in turn are using different versions of the same dependency, and this problem is present, I don't see how I could go around it. – palako Dec 23 '20 at 03:06
  • @palako yes, it will probably require crate authors to make use of it. However, it could be implemented as defaulting to public, in which case versions would need to be unified across all dependencies. It could also allow the end user to define the public/private boundaries for crates that don’t define it. For your example, it all depends on how those crates use the dependency – Shepmaster Dec 23 '20 at 03:11