servo / euclid

Geometry primitives (basic linear algebra) for Rust
Other
458 stars 102 forks source link

DeserializeOwned causes "trait `for<'de> serde::de::Deserialize<'de>` is not implemented" #391

Closed j1elo closed 4 years ago

j1elo commented 4 years ago

I'm trying to deserialize a Point3D using the recap crate, like this:

Cargo.toml

[dependencies]
euclid = "0.20.6"
recap = "0.1.1"
serde = { version = "1.0", features = ["derive"] }

main.rs

use std::io::{self, BufRead};
use euclid::*;
use recap::{Regex, from_captures};
use serde::Deserialize;

type Point3D = euclid::default::Point3D<i32>;

fn main() {
    let points: Vec<Point3D> = io::stdin()
        .lock() // Give access to BufRead::lines()
        .lines()
        .map(|l| l.expect("lines"))
        .map(|l| parse_line(&l))
        .collect();
}

// Simplified fn with unwrap() for clarity
fn parse_line(line: &str) -> Point3D {
    let pattern =
        Regex::new(r#"<x=(?P<x>-?\d+), y=(?P<y>-?\d+), z=(?P<z>-?\d+)>"#)
            .unwrap();
    let point: Point3D = from_captures(&pattern, line).unwrap();
    point
}

However I get this error in from_captures:

error[E0277]: the trait bound `for<'de> euclid::point::Point3D<i32, euclid::UnknownUnit>: serde::de::Deserialize<'de>` is not satisfied
   --> test/src/main.rs:73:26
    |
73  |     let point: Point3D = from_captures(&pattern, line).unwrap();
    |                          ^^^^^^^^^^^^^ the trait `for<'de> serde::de::Deserialize<'de>` is not implemented for `euclid::point::Point3D<i32, euclid::UnknownUnit>`
    | 
   ::: /home/test/.cargo/registry/src/github.com-1ecc6299db9ec823/recap-0.1.1/src/lib.rs:110:8
    |
110 |     D: DeserializeOwned,
    |        ---------------- required by this bound in `recap::from_captures`
    |
    = note: required because of the requirements on the impl of `serde::de::DeserializeOwned` for `euclid::point::Point3D<i32, euclid::UnknownUnit>`

I've been reading the code trying to understand what is going on, as it seems that Serde has a default implementation of DeserializeOwned for all types that implement Deserialize<'de>:

(from serde)

pub trait DeserializeOwned: for<'de> Deserialize<'de> {}
impl<T> DeserializeOwned for T where T: for<'de> Deserialize<'de> {}

and I can see that euclid seems to implement Deserialize<'de>, in point.rs:

#[cfg(feature = "serde")]
impl<'de, T, U> serde::Deserialize<'de> for Point3D<T, U>
    where T: serde::Deserialize<'de>
{
    [...]
}

However I'm a total beginner learning Rust, and I'm just learning to interpret trait implementations and lifetimes, so I'm not able to work out by myself what is wrong and what should be done to fix it, in case it's not an actual bug in euclid (is it?)

j1elo commented 4 years ago

DeserializeOwned comes from the from_captures() function, which has this prototype:

pub fn from_captures<D>(
    re: &Regex,
    input: &str,
) -> Result<D, Error>
where
    D: DeserializeOwned,
{
    [...]
}
nical commented 4 years ago

Hi, serialization/deserialization is an optional feature in euclid. You can enable it in your cargo.toml by replacing euclid = "0.20.6" (or whatever version you depend on) with euclid = { version = "0.20.6", features = ["serde"] }

j1elo commented 4 years ago

Hi, serialization/deserialization is an optional feature in euclid. You can enable it in your cargo.toml by replacing euclid = "0.20.6" (or whatever version you depend on) with euclid = { version = "0.20.6", features = ["serde"] }

Thanks a lot! It solved the compilation issue. For the next time that something similar happens, how should I have been able to discover that serde is an optional feature of euclid that has to be enabled?

(also the problem now changed to a runtime error because the sample code shown fails with this error: invalid type: map, expected a tuple of size 3; this is me not knowing how to use the lib and not really a bug (so far), so it should be talked about in a user forum, is there a specific one for euclid?)

nical commented 4 years ago

For the next time that something similar happens, how should I have been able to discover that serde is an optional feature of euclid that has to be enabled?

Good question. I don't have good answer for it. Ideally the crate's documentation would explain what the optional feature flags are and how they affect the API, but euclid does a poor job of it. Once you get into reading the code, when you see #[cfg(feature="foo")] it means that the following item is only compiled if the feature "foo" is enabled.

also the problem now changed to a runtime error

I don't know anything about recap, but from this error it seems that the format you are providing in your example doesn't match the form it expects for deserialization. You could try to serialize something with the serializer used in recap and compare what the result looks like with the format you are using in your example.

j1elo commented 4 years ago

I saw some #[cfg(feature="foo")] but didn't know those meant features to be enabled in Cargo.toml. So, nice! thanks for the tips.

This is not a bug in euclid so I'm closing this issue.