koute / stdweb

A standard library for the client-side Web
Apache License 2.0
3.45k stars 177 forks source link

Can't use TryInto for custom struct when wrapped in Option #281

Closed ASalvail closed 6 years ago

ASalvail commented 6 years ago

Deserialization, using .try_into() doesn't seem to work for types wrapped into the Option enum because of a mismatch between the conversion errors.

This minimal example, based on the example from js_deserializable! illustrates the problem:

#[macro_use]
extern crate stdweb;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

use stdweb::unstable::TryInto;

#[derive(Deserialize, Debug)]
struct Person {
    name: String,
    age: i32
}

js_deserializable!( Person );

fn main() {
    let bob = js! {
        return {
            name: "Bob",
            age: 33
        };
    };

    let structure: Option<Person> = bob.try_into().unwrap();
}

leads to

error[E0271]: type mismatch resolving `<Person as stdweb::unstable::TryFrom<stdweb::Value>>::Error == stdweb::private::ConversionError`
  --> src\main.rs:26:41
   |
26 |     let structure: Option<Person> = bob.try_into().unwrap();
   |                                         ^^^^^^^^ expected struct `stdweb::serde::ConversionError`, found enum `stdweb::private::ConversionError`
   |
   = note: expected type `stdweb::serde::ConversionError`
              found type `stdweb::private::ConversionError`
   = note: required because of the requirements on the impl of `stdweb::unstable::TryFrom<stdweb::Value>` for `std::option::Option<Person>`
   = note: required because of the requirements on the impl of `stdweb::unstable::TryInto<std::option::Option<Person>>` for `stdweb::Value`

error[E0277]: the trait bound `Person: std::convert::AsRef<stdweb::Reference>` is not satisfied
  --> src\main.rs:26:41
   |
26 |     let structure: Option<Person> = bob.try_into().unwrap();
   |                                         ^^^^^^^^ the trait `std::convert::AsRef<stdweb::Reference>` is not implemented for `Person`
   |
   = note: required because of the requirements on the impl of `stdweb::unstable::TryFrom<stdweb::Value>` for `std::option::Option<Person>`
   = note: required because of the requirements on the impl of `stdweb::unstable::TryInto<std::option::Option<Person>>` for `stdweb::Value`
koute commented 6 years ago

Yes, this is a known problem and AFAIR cannot be fixed without breaking backwards compatibility.

For now you can simply use the Serde newtype wrapper, e.g.:

let structure: Option<Serde<Person>> = bob.try_into().unwrap();
ASalvail commented 6 years ago

Alright, that's a nice workaround. For anyone that might fall on this thread, here's the fixed version:

#[macro_use]
extern crate stdweb;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;

use stdweb::unstable::TryInto;
use stdweb::serde::Serde;

#[derive(Deserialize, Debug)]
struct Person {
    name: String,
    age: i32
}

js_deserializable!( Person );

fn main() {
    let bob = js! {
        return {
            name: "Bob",
            age: 33
        };
    };

    let structure: Option<Serde<Person>> = bob.try_into().unwrap();
    let opt_pers: Option<Person> = structure.map(|x| x.0);
}

Thanks @koute