Why is a trait not implemented for a type that clearly has it implemented?
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.
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.