Asked  6 Months ago    Answers:  5   Viewed   74 times

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.

 Answers

85

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.

Tuesday, June 1, 2021
 
iceduck
answered 6 Months ago
19

T, &T, and &mut T are all different types; and that means that &mut &mut T is likewise a different type. Traits are not automatically implemented for references to a type. If you wish to implement a trait for either of the references, you need to write it out explicitly.

As an example, this exhibits the same problem:

trait Foo {}

#[derive(Debug, Copy, Clone)]
struct S;
impl Foo for S {}

fn example<T>(_: T)
where
    T: Foo,
{}

fn main() {
    let mut s = S;

    example(s);
    example(&s);     // the trait bound `&S: Foo` is not satisfied
    example(&mut s); // the trait bound `&mut S: Foo` is not satisfied
}

Explicit implementations of the trait for the references solve the problem:

impl<'a> Foo for &'a S {}
impl<'a> Foo for &'a mut S {}

In many cases, you can delegate the function implementations to the non-reference implementation.

If this should always be true, you can make it so by applying it to all references to a type that implements a trait:

impl<'a, T> Foo for &'a T where T: Foo {}
impl<'a, T> Foo for &'a mut T where T: Foo {}

If you don't have control over the traits, you may need to specify that you take a reference to a generic type that implements the trait:

fn example<T>(_: &mut T)
where
    for<'a> &'a mut T: Foo,
{}

See also:

  • When should I not implement a trait for references to implementors of that trait?
Tuesday, August 3, 2021
 
ranhan
answered 4 Months ago
59

Serde can deserialize to a Rust struct from sequence-like structures as well as map-like ones.

Your structs are almost right, but there is an extra layer of hierarchy in your JSON. If your JSON was:

{
    "id": 1,
    "a": [1.2345, 5, 9.8765]
}

then this will just work, with the right serde annotations:

use serde::{Serialize, Deserialize};

#[derive(Deserialize)]
struct Ask {
    price: f64,
    whole_lot_volume: i64,
    lot_volume: f64,
}

#[derive(Deserialize)]
struct MyStruct {
    id: i32,
    a: Ask,
}

If you can't change the JSON, you can use an extra layer of structs to match:

use serde::{Deserialize, Serialize};

#[derive(Debug, Deserialize)]
struct Order {
    price: f64,
    whole_lot_volume: i64,
    lot_volume: f64,
}

#[derive(Debug, Deserialize)]
struct Ask {
    a: Order,
}

#[derive(Debug, Deserialize)]
struct MyStruct {
    id: i32,
    a: Ask,
}

It is rare that you need to implement your own Visitor; the Deserialize macro provided by serde is quite customisable. However, if you want to omit the extra struct, that's what you'd have to do.

You may need to do more work if some of the numbers are represented as JSON strings, but you can still do that without a custom Visitor implementation. See:

  • How to transform fields during deserialization using Serde?
  • How to transform fields during serialization using Serde?
  • Serde field attributes
Saturday, August 21, 2021
 
alioygur
answered 4 Months ago
97

Update: fixed object safety rules to the 1.0 version of them. Namely, by-value self makes method object-unsafe no longer.

This error happens because of object safety.

In order to be able to create a trait object out of a trait, the trait must be object-safe. A trait is object-safe if both of these statements hold:

  1. it does not have Sized requirement, as in trait Whatever: Sized {};
  2. all its methods are object-safe.

A method is object-safe if both of these statements are true:

  1. it has where Self: Sized requirement, as in fn method() where Self: Sized;
  2. none of the following statements holds:

    1. this method mentions Self in their signature in any form, even under a reference, except associated types;
    2. this method is static;
    3. this method is generic.

These restrictions are in fact fairly natural if you think more of them.

Remember that when values are made into trait objects, actual information of their type is erased, including their size. Therefore, trait objects can only be used through a reference. References (or other smart pointers, like Box or Rc), when applied to trait objects, become "fat pointers" - along with the pointer to the value, they also contain a pointer to the virtual table for that value.

Because trait objects can only be used through a pointer, by-value self methods can't be called on them - you'd need the actual value in order to call such methods. This was a violation of object safety at one point, which meant that traits with such methods couldn't be made trait objects, however, even before 1.0 the rules had been tweaked to allow by-value self methods on trait objects. These methods still can't be called, though, due to the reason described above. There are reasons to expect that in the future this restriction will be lifted because it currently leads to some quirks in the language, for example, the inability to call Box<FnOnce()> closures.

Self can't be used in methods which should be called on trait objects precisely because trait objects have their actual type erased, but in order to call such methods the compiler would need to know this erased type.

Why static methods can't be called on trait objects, I guess, is obvious - static methods by definition "belong" to the trait itself, not to the value, so you need to know the concrete type implementing the trait to call them. More concretely, regular methods are dispatched through a virtual table stored inside a trait object, but static methods do not have a receiver, so they have nothing to dispatch on, and for this reason they can't be stored in a virtual table. Thus they are uncallable without knowing the concrete type.

Generic trait methods can't be called for another reason, more technical than logical, I think. In Rust generic functions and methods are implemented through monomorphization - that is, for each instantiation of a generic function with a concrete set of type parameters the compiler generate a separate function. For the language user it looks like that they're calling a generic function; but on the lowest level for each set of type parameters there is a separate copy of the function, specialized to work for the instantiated types.

Given this approach, in order to call generic methods on a trait object you would need its virtual table to contain pointers to virtually each and every possible instantiation of the generic method for all possible types, which is, naturally, impossible because it would require infinite number of instantiations. And so calling generic methods on trait objects is disallowed.

If Drawable is an external trait, then you're stuck - it is impossible to do what you want, that is, to call draw() on each item in a heterogeneous collection. If your set of drawables is statically known, you can create a separate collection for each drawable type or, alternatively, create your own enum which would contain a variant for each drawable type you have. Then you can implement Drawable for the enum itself, which would be fairly straightforward.

Sunday, August 22, 2021
 
jdmcbr
answered 4 Months ago
71

According to JSONs specification, JSON keys must be strings. serde_json uses fmt::Display in here, for some non-string keys, to allow serialization of wider range of HashMaps. That's why HashMap<u64, f64> works as well as HashMap<String, f64> would. However, not all types are covered (Foo's case here).

That's why we need to provide our own Serialize implementation:

impl Display for Foo {
    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
        write!(f, "{}", self.x)
    }
}

impl Serialize for Bar {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut map = serializer.serialize_map(Some(self.x.len()))?;
        for (k, v) in &self.x {
            map.serialize_entry(&k.to_string(), &v)?;
        }
        map.end()
    }
}

(playground)

Sunday, September 19, 2021
 
blacksite
answered 3 Months ago
Only authorized users can answer the question. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :  
Share