Crate serde_closure
source ·Expand description
Serializable and debuggable closures.
📦 Crates.io │ 📑 GitHub │ 💬 Chat
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
andFn
, 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
andDebug
. - 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
.
Modules
FnOnce
, FnMut
and
Fn
macros. They implement traits::FnOnce
,
traits::FnMut
and traits::Fn
respectively (and std::ops::FnOnce
, std::ops::FnMut
and std::ops::Fn
on nightly), as well as Debug
, Serialize
and Deserialize
, and various convenience traits.std::ops::FnOnce
, std::ops::FnMut
and
std::ops::Fn
that are usable on stable Rust. They are implemented
by closures created by the FnOnce
,
FnMut
and Fn
macros.Macros
Fn
struct that
implements traits::Fn
(and std::ops::Fn
on nightly),
Debug
, Serialize
and
Deserialize
, and various convenience traits.FnMut
struct
that implements traits::FnMut
(and std::ops::FnMut
on nightly),
Debug
, Serialize
and
Deserialize
, and various convenience traits.FnOnce
struct that implements traits::FnOnce
(and std::ops::FnOnce
on
nightly), Debug
, Serialize
and
Deserialize
, and various convenience traits.Attribute Macros
FnOnce(…) -> …
, FnMut(…) -> …
and Fn(…) -> …
to FnOnce<(…), Output = …>
,
FnMut<(…), Output = …>
and Fn<(…), Output = …>
. This is just a
convenience to enable parenthesized arguments for non std::ops::*
traits
on stable Rust.