Giter Site home page Giter Site logo

serde_closure's Introduction

serde_closure

Crates.io MIT / Apache 2.0 licensed Build Status

๐Ÿ“– Docs | ๐Ÿ’ฌ Chat

Serializable and debuggable closures.

This library provides macros that wrap closures to make them serializable and debuggable.

use serde_closure::{traits::Fn, Fn};

let one = 1;
let plus_one = Fn!(|x: i32| x + one);

assert_eq!(2, plus_one.call((1,))); // this works on stable and nightly
// assert_eq!(2, plus_one(1));      // this only works on nightly
println!("{:#?}", plus_one);

// prints:
// Fn<main::{{closure}} at main.rs:6:15> {
//     one: 1,
//     source: "| x : i32 | x + one",
// }

This library aims to work in as simple and safe a way as possible. On stable Rust the wrapped closures implement traits::FnOnce, traits::FnMut and traits::Fn, and on nightly Rust std::ops::FnOnce, std::ops::FnMut and std::ops::Fn are implemented as well using the unboxed_closures and fn_traits features (rust issue #29625).

  • There are three macros, FnOnce, FnMut and Fn, corresponding to the three types of Rust closure.
  • Wrap your closure with one of the macros and it will now implement Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize and Debug.
  • There are some minor syntax limitations, which are documented below.
  • This crate has one unavoidable but documented and sound usage of unsafe.

Examples of wrapped closures

Inferred, non-capturing closure:

|a| a+1
FnMut!(|a| a+1)

Annotated, non-capturing closure:

|a: String| -> String { a.to_uppercase() }
FnMut!(|a: String| -> String { a.to_uppercase() })

Inferred closure, capturing num:

let mut num = 0;
|a| num += a
let mut num = 0;
FnMut!(|a| num += a)

move closure, capturing hello and world:

let hello = String::from("hello");
let mut world = String::new();
move |name| {
    world += (hello.to_uppercase() + name).as_str();
}
let hello = String::from("hello");
let mut world = String::new();
FnMut!(move |name| {
    world += (hello.to_uppercase() + name).as_str();
})

Limitations

There are currently some minor limitations:

  • Use of types that start with a lowercase letter need might need to be disambiguated from variables. If you see an error like the following, fix the case of the type, or append it with my_struct::<> to disambiguate.
error[E0308]: mismatched types
   --> tests/test.rs:450:4
    |
449 |       FnOnce!(move || {
    |  _____-
450 | |         my_struct;
    | |         ^^^^^^^^^ expected struct `serde_closure::internal::a_variable`, found struct `my_struct`
451 | |     });
    | |______- in this macro invocation
    |
    = note: expected type `serde_closure::internal::a_variable`
               found type `my_struct`
  • Use of variables that start with an uppercase letter might need to be disambiguated from types. If you see an error like the following, fix the case of the variable, or wrap it with (MyVariable) to disambiguate.
error: imports cannot refer to local variables
   --> tests/test.rs:422:3
    |
417 |       FnOnce!(move || {
    |  _____-
418 | |         MyVariable;
    | |         ^^^^^^^^^^
419 | |     });
    | |______- in this macro invocation
    |
  • Functions and closures called inside the closure might need to be disambiguated. This can be done the same as above with function::<> for functions and (closure) for closures.

Serializing between processes

Closures created by this crate are unnameable โ€“ i.e. just like normal closures, there is no Rust syntax available with which to write the type. What this means is that to deserialize a closure, you either need to specify the precise type you're deserializing without naming it (which is possible but not particularly practical), or erase the type by storing it in a trait object.

The serde_traitobject crate enables trait objects to be safely serialized and sent between other processes running the same binary.

For example, if you have multiple forks of a process, or the same binary running on each of a cluster of machines, serde_traitobject would help you to send serializable closures between them. This can be done by upcasting the closure to a Box<dyn serde_traitobject::Fn()>, which is automatically serializable and deserializable with serde.

License

Licensed under either of

at your option.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

serde_closure's People

Contributors

alecmocatta avatar mergify[bot] avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar

serde_closure's Issues

How to serialize and Deserialize closure?

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?

Closures are empty when serialized - am I doing something wrong?

Hey mate,

I think I might be doing something wrong. Suppose I have the following minimal example:

use serde_closure::*;

fn main() {
    let closure = Fn!(|x: i32| x + 1);
    println!("closure is {:?}", closure);

    let closure_str = serde_json::to_string(&closure).unwrap();
    println!("Serialised closure is {}", closure_str);
}

the output I get is:

closure is Fn<serde_test::main::{{closure}} at src/main.rs:4:19> { source: "| x : i32 | x + 1" }
Serialised closure is {}

Is this wrong or am I missing something? Here is my Cargo.toml:

[package]
name = "serde_test"
version = "0.1.0"
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
ron = "~0.6"
serde = "1.0"
serde_closure = "0.3.2"
serde_json = "~1.0"
serde_traitobject = "0.2.3"

no maccro rules for tuples with types as closure arguments

Thanks for this wonderful library. I am dealing with generic closure like this.

Fn(T) -> U

Therefore I can only use tuples as closure argument. When types are inferred there is no problem with Fn! macro. But when I have to provide type annotations, it is not compiling.

let f = Fn!(|(x, y): (i32, i32)| -> (String, i32) { ((x + 2).to_string(), y + 2) });

Can you please provide some solutions? It would be really helpful for me.

Correctly handle macro invocations in closures

Currently this crate doesn't necessarily correctly handle macro invocations that expand to code that includes captured variables.

Doing this correctly requires eagerly evaluating macro invocations, see rust-lang/rfcs#2320

Currently this crate assumes macro arguments are comma-separated expressions, emitting a warning if that doesn't hold.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.