maybe-fut
Introduction
Maybe-fut is a Rust library that provides a way to export both a sync and an async API from the same codebase. It allows you to write your code once and have it work in both synchronous and asynchronous contexts.
This is achieved through a complex mechanism of proc macros and wrappers around tokio and std libraries.
Maybe-fut provides its own type library, for fs, io, net, sync and time modules, which are designed to use std or tokio types as needed. Mind that for compatibility reasons, the io module has been re-implemented from scratch.
At runtime it checks whether the thread is running in a sync or async context and calls the appropriate function. This allows you to write your code once and have it work in both synchronous and asynchronous contexts.
This is a simple example of how it works:
-
Setup your logic to be exported using
maybe-futtypes:use ; use File;If you see there is an attribute macro there, called
maybe_fut. This macro takes 3 arguments:sync: The name of the sync struct that will be generated.tokio: The name of the async struct that will be generated.tokio_feature: The name of the feature that will be used to enable the async struct.
-
Users can now access the public API exported from the library:
async
A full example can be found in the examples folder and can be run using the following command:
And the maybe_fut macro can be applied to traits as well, even combining generics:
use Display;
/// A trait to greet the user.
Performance
As of now, the performance of maybe-fut is on par with the tokio and std libraries. The proc macro generates code that is optimized for both synchronous and asynchronous contexts, so there is no significant overhead when using it.
This overhead is negligible, and it has been benchmarked, as shown at maybe-fut/benches/async_context.rs, and the results are actually quite good.
is_async_context time: [3.3511 ns 3.3567 ns 3.3621 ns]
Found 4 outliers among 100 measurements (4.00%)
3 (3.00%) high mild
1 (1.00%) high severe
What's the cost of a wrapper? Let's try to measure it by creating a file with tokio::fs::File::create and maybe_fut::fs::File::create:
tokio_create_file time: [11.529 µs 11.620 µs 11.715 µs]
Found 4 outliers among 100 measurements (4.00%)
4 (4.00%) high mild
maybe_fut_create_file time: [11.603 µs 11.696 µs 11.786 µs]
So yeah, the cost of the wrapper is a very little higher, but the difference is negligible.
Limitations
Currently, there are some limitations with the proc macro, so the following features are still not supported:
- Builders (e.g.
fn foo(mut self) -> Self) - Derive of the inner type
Support the developer
If you like maybe-fut, please consider a little donation 🥳
Changelog
License
Licensed under MIT license (SEE LICENSE or http://opensource.org/licenses/MIT)