Expand description
“Magic” declarative-style function arguments.
fn f0() { /* ... */ }
fn f1(x: i32) { /* ... */ }
fn f2(x: i32, z: usize) { /* ... */ }
async fn f3(y: &'static str) { /* ... */ }
async fn f4(y: &'static str, x: i32, z: usize) { /* ... */ }
#[derive(MagicArgs)]
struct Args(i32, &'static str, usize);
let args = Args(-42, "🦀", 42);
let _ = apply(f0, &args);
let _ = apply(f1, &args);
let _ = apply(f2, &args);
let _ = apply(f3, &args);
let _ = apply(f4, &args);The original idea for this crate comes from axum’s route handlers, which do this exact thing with their arguments.
§Quick start
- You define a “set of arguments”.
#[derive(MagicArgs)]
pub struct MyArgs(&'static str, usize /*, ... */);
let args = MyArgs("Hello, world!", 42);- You use
applyto call a function of your choosing.
fn my_func(msg: &'static str) { /* ... */ }
let result = apply(my_func, args);- Profit? 💰
*this product contains limitations which are not featured above
§How it works
All of the “magic” of this crate is done via Callable and Args.
Everything else is purely cosmetic and for convinience.
This documentation will use the term “argument set” to refer to any type $T$
which implements Args<U> for some $U$.
Callable is defined in terms of $A$ and $T$. $A$ is the argument set
which contains all arguments available to the function. $T$ is an ordered
tuple containing the arguments the function expects to be given.
Callable is implemented automatically for all FnOnce(T0, T1, ...) -> O
(up to 32 arguments). The job of the Callable trait is to pick out what
arguments the function wants from a given $A$. For this to be possible, $A$
must be bounded such that $T \subseteq A$. In other words; to be able to
even call the function with the arguments it expects, we must have the
arguments in the first place. For example, let $T = (T_0, T_1, T_2)$. It
must be that $T_0, T_1, T_2 \in A$ or, in code,
A: Args<T0> + Args<T1> + Args<T2>. Picking the correct argument from the
argument set $A$ for each argument of the function is done with
Args::get and the compiler’s type inference.
§Limitations
This whole crates operates on the type-level of arguments. For this reason, it is impossible to have a function which accepts 2 different instances of the same type. To see why this is, consider the following function:
fn f(x: i32, _y: i32) -> i32 { x }Given its signature $f: (i32, i32) \to i32$ and an arguments set
$A = \{ 42, -42 \}$. It is impossible to know the correct order of passing
these values to $f$ such that it returns 42, or -42 for that matter.
This means all values in the argument set must be of a different type. In
cases like above when a function has 2 arguments of the same type, the
value of the matching type in the argument set is passed twice. This incurs
a Clone::clone call.
Traits§
- Args
- A “set of arguments” that contains
T. - Callable
- A trait to describe any kind of type that can be called.
- Magic
Args - A convinience trait to provide the
args.apply(f)syntax.
Functions§
- apply
- Apply f on
args.
Derive Macros§
- Magic
Args derive - A derive macro to help you create argument sets.