serde_closure icon indicating copy to clipboard operation
serde_closure copied to clipboard

How to serialize and Deserialize closure?

Open TrionProg opened this issue 4 years ago • 9 comments

Hello, I am trying to do it.

You wrote:

you either need to specify the precise type you're deserializing without naming it (which is possible but not particularly practical)

How to do it(to deserialize)?

I use serde_traitobject and write this code

use serde_traitobject as st;

let one:usize = 1;
let two:usize = 2;
let plus_one = Fn!(|x: usize| x + one + two);

let s: st::Box<dyn serde_traitobject::Fn(usize) -> usize> = st::Box::new(plus_one);

let serialized = serde_json::to_string(&s).unwrap();
println!("{}",serialized);

let deserialized: st::Box<dyn serde_traitobject::Fn(usize) -> usize> = serde_json::from_str(serialized.as_str()).unwrap();

And I have error: the trait bound `&usize: main::_IMPL_DESERIALIZE_FOR_MyStruct::_serde::Deserialize<'_>` is not satisfied

What should I do?

Can I create struct with two or more closures and serialize and deserialize it? Without Box<dyn ... Fn> just Box<MyStructTrait>?

TrionProg avatar Jan 11 '20 16:01 TrionProg

Hi @TrionProg, thanks for filing an issue!

I think the problem is that the closure here needs to be a move closure to take ownership of the captured variables (one and two), rather than take references to them. This is because it's not possible to deserialize to a reference (your error message shows it's trying to deserialize to a &usize, which isn't possible).

This should work:

let plus_one = Fn!(move |x: usize| x + one + two);

Can I create struct with two or more closures and serialize and deserialize it? Without Box<dyn ... Fn> just Box?

Boxing the closure's trait object, i.e. Box<dyn ... Fn>, is necessary in most use cases. A struct containing multiple closures could look like:

struct MultipleClosures {
    a: Box<dyn st::Fn(usize) -> usize>,
    b: Box<dyn st::Fn(usize) -> usize>,
}

alecmocatta avatar Jan 15 '20 11:01 alecmocatta

Hello. Hm. Yes, I've forgotten move. And now it works.

Unfortunately I need use 2 boxes in the struct, + 1 box for this struct. Later, I hope, I will try to optimize it to MultipleClosures<F1,F2>, this should be simmilar to error_impl in failure library:

Box<Inner<Fail>> 
struct Inner<E:Fail> {
  backtrace
  error:E
}

Thank you for your fantastic library!

TrionProg avatar Jan 22 '20 21:01 TrionProg

Hello. Ohh, not all is right. I can not show the code -- it has a lot of garbage and I can not understand anything that happens -- All works, but then I implement Trait, I have a lot of problems like Self: traitobject::Sealed is not satisfied.

Can you give me an example how to: Create trait(MyTrait), that wants Self(MultipleClosures) be serializable (by your library). And Just create MultipleClosures, insert it in Box<dyn MyTrait>and it will serialize and deserialize it. MultipleClosures has one generic as parameter, It should not be stored in this struct, for example, closure a returns it.

TrionProg avatar Jan 23 '20 01:01 TrionProg

Okey. I am making experiment. Minimal example of same error.

#![feature(unboxed_closures)]

extern crate serde;
extern crate serde_traitobject;

#[macro_use]
extern crate serde_closure;

#[macro_use]
extern crate serde_derive;

use serde_traitobject::FnOnce as FnOnceTO;
use serde_traitobject::Box as BoxTO;

use serde_traitobject::{
    Serialize as SerializeTO,
    Deserialize as DeserializeTO
};

use serde_derive::{Serialize, Deserialize};

use std::marker::PhantomData;

pub trait BlockTrait: SerializeTO + DeserializeTO{
    fn exec(&mut self) -> bool;
}

#[derive(Serialize, Deserialize)]
struct TwoClosures<T> where
    T:'static
{
    inner: Option<TwoClosuresInner<T>>
}

#[derive(Serialize, Deserialize)]
struct TwoClosuresInner<T> where
    T:'static
{
    #[serde(with = "serde_traitobject")]
    a: Box<FnOnceTO() -> T>,
    #[serde(with = "serde_traitobject")]
    b: Box<FnOnceTO(T) -> bool>,
    _phantom_data:PhantomData<T>
}

impl<T> TwoClosures<T> where
{
    fn new<A, B>(a:A, b:B) -> Self where
        A: FnOnceTO() -> T + 'static,
        B: FnOnceTO(T) -> bool + 'static
    {
        let inner = TwoClosuresInner {
            a: Box::new(a),
            b: Box::new(b),
            _phantom_data: PhantomData
        };

        TwoClosures {
            inner: Some(inner)
        }
    }
}

impl<T> BlockTrait for TwoClosures<T> {
    fn exec(&mut self) -> bool {
        let inner = self.inner.extract();
        let t = (inner.a)();
        (inner.b)(t)
    }
}

fn main() {
    let tc = TwoClosures::new(
        FnOnce!(|| 8usize),
        FnOnce!(move|v|v==8),
    );

    let tc_box:BoxTO<dyn BlockTrait> = BoxTO::new(tc);

    let serialized = serde_json::to_string(&tc_box).unwrap();
    println!("{}",serialized);
}

And errors. It wants T to be serializable, but it is 'static.

error[E0277]: the trait bound `TwoClosures<T>: serde_traitobject::serialize::Sealed` is not satisfied
  --> src\main.rs:67:9
   |
67 | impl<T> BlockTrait for TwoClosures<T> {
   |         ^^^^^^^^^^ the trait `serde_traitobject::serialize::Sealed` is not implemented for `TwoClosures<T>`

error[E0277]: the trait bound `T: serde::Serialize` is not satisfied
  --> src\main.rs:67:9
   |
67 | impl<T> BlockTrait for TwoClosures<T> {
   |      -  ^^^^^^^^^^ the trait `serde::Serialize` is not implemented for `T`
   |      |
   |      help: consider restricting this bound: `T: serde::Serialize`
   |
   = note: required because of the requirements on the impl of `serde::Serialize` for `TwoClosures<T>`
   = note: required because of the requirements on the impl of `serde_traitobject::Serialize` for `TwoClosures<T>`

error[E0277]: the trait bound `T: serde::Deserialize<'_>` is not satisfied
  --> src\main.rs:67:9
   |
67 | impl<T> BlockTrait for TwoClosures<T> {
   |      -  ^^^^^^^^^^ the trait `serde::Deserialize<'_>` is not implemented for `T`
   |      |
   |      help: consider restricting this bound: `T: serde::Deserialize<'_>`
   |
   = note: required because of the requirements on the impl of `for<'de> serde::Deserialize<'de>` for `TwoClosures<T>`
   = note: required because of the requirements on the impl of `serde::de::DeserializeOwned` for `TwoClosures<T>`
   = note: required because of the requirements on the impl of `serde_traitobject::Deserialize` for `TwoClosures<T>`

UPD.

I want to do not haul this 'de, but I try:

pub trait BlockTrait<'de>: Serialize + Deserialize<'de>{
    fn exec(&mut self) -> bool;
}

impl<'de, T> BlockTrait<'de> for TwoClosures<T> {
    fn exec(&mut self) -> bool {
        let inner = self.inner.extract();
        let t = (inner.a)();
        (inner.b)(t)
    }
}

And it wants T:Serialize, T:Deserialize =(

TrionProg avatar Jan 24 '20 06:01 TrionProg

And second question: how to serialize struct TwoClosures by may hands without derive?

TrionProg avatar Jan 24 '20 06:01 TrionProg

To make that minimal example work, you need to add #[serde(bound = "")] like this:

#[derive(Serialize, Deserialize)]
#[serde(bound = "")]
struct TwoClosures<T> where
    T:'static
{
    inner: Option<TwoClosuresInner<T>>
}

Without it, serde is generating:

impl<T> Serialize for TwoClosures<T> where T: Serialize { ... }

Though as actually T itself is never serialized, the bound is unnecessary. Adding #[serde(bound = "")] makes serde generate this:

impl<T> Serialize for TwoClosures<T> { ... }

which then enables your program to compile.

alecmocatta avatar Jan 24 '20 10:01 alecmocatta

And second question: how to serialize struct TwoClosures by may hands without derive?

The serde guide to implementing Serialize and Deserialize are helpful here. The easiest way to serialize/deserialize them manually is as tuples.

alecmocatta avatar Jan 24 '20 10:01 alecmocatta

Hello. Hm.. It works! Thanks.

About Box in structures. What I should to use std::Box or s_to::Box? What is differense? Now it is using std::Box and it works, but maybe I should use s_to::Box. But how I remember I was unable to serialize std::Box manually.

TrionProg avatar Jan 25 '20 15:01 TrionProg

Long live to new problems!

My manual serialization worked.

I am adding new generic M

pub trait BlockTrait<M:MyTrait>: SerializeTO + DeserializeTO{
    fn exec(&mut self, m:&mut M) -> bool;
}

pub trait MyTrait {}

struct TwoClosures<T, M> where
    T:'static,
    M:MyTrait + 'static
{
    inner: DataContainer<TwoClosuresInner<T, M>>
}

struct TwoClosuresInner<T, M> where
    T:'static,
    M:MyTrait + 'static
{
    a: BoxTO<FnOnceTO(&mut M) -> T>,
    b: BoxTO<FnOnceTO(T) -> bool>,
}

And I have this problem:

error[E0277]: the trait bound `dyn for<'r> serde_traitobject::FnOnce<(&'r mut M,), Output = T>: serde::Serialize` is not satisfied
   --> src\main.rs:107:32
    |
107 |         s.serialize_field("a", &inner.a)?;
    |                                ^^^^^^^^ the trait `serde::Serialize` is not implemented for `dyn for<'r> serde_traitobject::FnOnce<(&'r mut M,), Output = T>`
    |
    = help: the following implementations were found:
              <(dyn serde_traitobject::FnOnce<Args, Output = Output> + 'static) as serde::Serialize>
              <(dyn serde_traitobject::FnOnce<Args, Output = Output> + std::marker::Send + 'static) as serde::Serialize>
    = note: required because of the requirements on the impl of `serde::Serialize` for `std::boxed::Box<dyn for<'r> serde_traitobject::FnOnce<(&'r mut M,), Output = T>>`

And something about Deser.

The fragment of code:

impl<T,M> Serialize for TwoClosures<T,M> where
    //T:'static,
    M:MyTrait
{
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
        where
            S: Serializer,
    {
        use crate::serde::ser::SerializeStruct;

        let inner = self.inner.get();

        let mut s = serializer.serialize_struct("TwoClosures", 2)?;
        s.serialize_field("a", &inner.a)?;
        s.serialize_field("b", &inner.b)?;
        s.end()
    }
}

It may be solved by using BoxTO instead of Box, but without M it worked! Very strange...

And I want M to be Serial and Deserial -able. How to mark it in trait MyBlock or trait MyTrait ? Note: Other difficult code works by #derive , but I have problems with manual serialization

TrionProg avatar Jan 26 '20 06:01 TrionProg