Function variable with dynamic function parameters - rust

Some libraries such as Bevy or Actix Web have functions which accept user defined functions with any amount of parameters.
Examples:
Actix Web:
async fn fn1(path: web::Path<String>) -> impl Responder {
// not important
}
async fn fn2(_req: HttpRequest) -> impl Responder {
// not important
}
let app = App::new()
.route("/", web::get().to(fn2))
.route("/{name}", web::get().to(fn1));
Bevy:
fn fn1(mut commands: Commands) {}
fn fn2(mut commands: Commands, time: Res<Time>) {}
App::new().add_system(fn1).add_system(fn2);
As you can see in both cases the functions web::get().to(), add_system() accept functions with dynamic number and types of parameters as their parameter. They're not macros. How can I achieve this? Is there a name for this? Thanks

Since functions can implement traits, the solution is to define a trait that represents "can serve as callback function" and then manually implement it for every function arity up to some large number of arguments. In actix, this is done by having .to(f) take something implementing the Handler trait:
pub fn to<F, Args>(mut self, handler: F) -> Self
where
F: Handler<Args>,
Args: FromRequest + 'static,
F::Output: Responder + 'static,
Where Handler is defined by:
pub trait Handler<Args>: Clone + 'static {
type Output;
type Future: Future<Output = Self::Output>;
fn call(&self, args: Args) -> Self::Future;
}
And implementations for function arities are created as follows:
/// Generates a [`Handler`] trait impl for N-ary functions where N is specified with a sequence of
/// space separated type parameters.
///
/// # Examples
/// ```ignore
/// factory_tuple! {} // implements Handler for types: fn() -> R
/// factory_tuple! { A B C } // implements Handler for types: fn(A, B, C) -> R
/// ```
macro_rules! factory_tuple ({ $($param:ident)* } => {
impl<Func, Fut, $($param,)*> Handler<($($param,)*)> for Func
where
Func: Fn($($param),*) -> Fut + Clone + 'static,
Fut: Future,
{
type Output = Fut::Output;
type Future = Fut;
#[inline]
#[allow(non_snake_case)]
fn call(&self, ($($param,)*): ($($param,)*)) -> Self::Future {
(self)($($param,)*)
}
}
});
factory_tuple! {}
factory_tuple! { A }
factory_tuple! { A B }
factory_tuple! { A B C }
factory_tuple! { A B C D }
factory_tuple! { A B C D E }
factory_tuple! { A B C D E F }
factory_tuple! { A B C D E F G }
factory_tuple! { A B C D E F G H }
factory_tuple! { A B C D E F G H I }
factory_tuple! { A B C D E F G H I J }
factory_tuple! { A B C D E F G H I J K }
factory_tuple! { A B C D E F G H I J K L }

Related

How to satisfy borrow checker with a value that is moved then immediately replaced in this unfold implementation?

The following is an unsatisfying implementation of unfold because the closure must take &A instead of A.
pub fn unfold<A, B>(init: A, f: impl Fn(&A) -> Option<(A, B)>) -> impl Iterator<Item = B> {
let mut acc = init;
std::iter::from_fn(move || {
f(&acc).map(|(a, b)| {
acc = a;
b
})
})
}
If the f call returns None, the loop exits and acc is used no longer. If it returns Some, acc is replaced with a new value.
Any way to have f take A instead of &A without unsafe, or Copy or other requirements?
Having f take A directly is not possible because if f panics, acc could be left in an undefined state.
A simple way to fix this is to make acc an Option<A>.
pub fn unfold2<A, B>(init: A, f: impl Fn(A) -> Option<(A, B)>) -> impl Iterator<Item = B> {
let mut acc = Some(init);
std::iter::from_fn(move || {
// The unwrap below is okay because `acc` being None means the iterator is over.
// The iterator API says that calling `next` on spent iterator can panic.
f(acc.take().unwrap()).map(|(a, b)| {
acc = Some(a);
b
})
})
}

Rust: Apply function n times

The problem
I want to create a function (chain) that takes a function f: Fn(i32) -> i32, number n and returns a new function g: Fn(i32) -> i32 that applys f to it's input n times.
For example: chain(3, f) should return a function eqvivalent to: |x| f(f(f(x))).
Here is my attempt at achieving this (similar code works in Python):
fn chain(n: i32, f: Box<dyn Fn(i32) -> i32>) -> Box<dyn Fn(i32) -> i32> {
println!("This is not printed when the resulting function is called!");
match n {
1 => f,
_ if n > 1 => {
let g = chain(n - 1, f);
Box::new(move |x| g(f(x)))
},
_ => Box::new(|x| x),
}
}
The compiler produces some errors about borrowing moved values, and I can't figure out how to fix them.
error[E0382]: use of moved value: `f`
--> src/lib.rs:7:22
|
1 | fn chain(n: i32, f: Box<dyn Fn(i32) -> i32>) -> Box<dyn Fn(i32) -> i32> {
| - move occurs because `f` has type `Box<dyn Fn(i32) -> i32>`, which does not implement the `Copy` trait
...
6 | let g = chain(n - 1, f);
| - value moved here
7 | Box::new(move |x| g(f(x)))
| ^^^^^^^^ - use occurs due to use in closure
| |
| value used here after move
Background
I want to create a program that takes an arithmetic expression in string form and then creates a function that can evaluate the expression. For example: "2*x+3" should result in a function |x| add(mul(2,x),3). The resulting function is called many times and should execute as fast as possible.
The easiest way to do that is to implement it with iteration instead of recursion:
fn chain(n: i32, f: Box<dyn Fn(i32) -> i32>) -> Box<dyn Fn(i32) -> i32> {
println!("This is not printed when the resulting function is called!");
Box::new (move |x| {
let mut r = f (x);
for _ in 1 .. n {
r = f (r);
}
r
})
}
Playground
For extra performance, this even allows you to get rid of the boxes and avoid the dynamic function calls:
fn chain(n: i32, f: impl Fn(i32) -> i32) -> impl Fn(i32) -> i32 {
println!("This is not printed when the resulting function is called!");
move |x| {
let mut r = f (x);
for _ in 1 .. n {
r = f (r);
}
r
}
}
Playground
You use f twice. You cannot. You cannot use a reference either, since the function is local and you cannot return a reference to it.
The simplest fix is to replace Box with Rc:
fn chain(n: i32, f: Rc<dyn Fn(i32) -> i32>) -> Rc<dyn Fn(i32) -> i32> {
println!("This is not printed when the resulting function is called!");
match n {
1 => f,
_ if n > 1 => {
let g = chain(n - 1, Rc::clone(&f));
Rc::new(move |x| g(f(x)))
},
_ => Rc::new(|x| x),
}
}
However, this incurs an overhead of a dynamic function call for each operation, an I think a simple match to the operation will be faster.

How can we store a chain of heterogeneous functions in Rust?

I'm working on some code where I'm interested in a lazy-evaluated function chain. In other words, it stores all the operations you want, and only evaluates them all together.
This is very easy when all the functions in the chain take the same type and return the same type. However, I'm stuck on how to make this work when the chain of functions returns a different type each time. This easy case can be seen in the following code:
struct FuncChain<T> {
funcs: Vec<fn(T) -> T>
}
impl<T> FuncChain<T> {
fn call(&self, input: T) -> T {
self.funcs.iter().fold(input, |prev, func| func(prev))
}
}
fn main(){
let fc = FuncChain {
funcs: vec![
|x| x + 1,
|x| x + 2,
|x| x * 2,
|x| x - 2,
]
};
println!("{}", fc.call(1));
}
(Playground)
So in this case we go i32 -> i32 -> i32 -> i32 -> i32.
What I want to do is a more general case where we go A -> B -> C -> D -> E, meaning that the funcs vector contains: fn(A) -> B, fn(B) -> C, fn(C) -> D, and fn(D) -> E. But how can this type definition be assigned to a struct? I can't create a vector with heterogeneous types, and even if I could, what would the type signature of the struct be?
I could make a recursive type definition perhaps, where the FuncChain holds a pointer to the first function object, and also the next object in the chain :
struct FuncChain<S, T, U> {
func: fn(S) -> T,
next: FuncChain<T, U, ?>
}
impl<S, T, U> FuncChain<S, T, U> {
fn call(&self, input: T) -> T {
self.funcs.iter().fold(input, |prev, func| func(prev))
}
}
fn main(){
let fc = FuncChain {
funcs: vec![
|x| x.toString(),
|x| u8::fromStr(x),
|x| x.toString(),
|x| i32::fromStr(x),
]
};
println!("{}", fc.call(1));
}
However of course this won't work, because I can't know the output type of next.
How can this be done?
You question is similar to Iterator, and so can be solved the same solution: a trait indicating a "callable".
The trait lets you "break" the infinite recursion of your current struct-based system, by having the struct just denote it as "whatever that does".
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f0d6bcc9eb8e070c1d9b6469f6a5e148
struct Chain<U, V, F> {
prev: F,
f: fn(U) -> V,
}
trait FuncChain<T, U> {
fn call(&self, _: T) -> U;
fn chain<V>(self, next: fn(U) -> V) -> Chain<U, V, Self>
where
Self: Sized,
{
Chain {
prev: self,
f: next,
}
}
}
impl<T, U> FuncChain<T, U> for fn(T) -> U {
fn call(&self, t: T) -> U {
self(t)
}
}
impl<T, U, V, F> FuncChain<T, V> for Chain<U, V, F>
where
F: FuncChain<T, U>,
{
fn call(&self, t: T) -> V {
(self.f)(self.prev.call(t))
}
}
fn main() {
let c = ((|x| x + 1) as fn(i32) -> i32)
.chain(|x| x * 2)
.chain(|x| x - 2);
println!("{}", c.call(5));
}
A better Rustacean can probably design a simpler way to achieve this.
If you're fine with using nightly, there's probably a way to use Fn instead of needing a custom trait.
Hell, fundamentally it's just . so you can probably manage with just a generic function and a closure, I'll have to check.

How can I pattern match a tuple containing a &mut enum and use the enum in the match arm?

How can the code below be made to compile? It seems perfectly safe, but can't convince the compiler that it is.
The version matching *self gives the error: cannot move out of borrowed content on the line of the match
The version matching self gives: use of moved value: *self
enum Foo {
Foo1(u32),
Foo2(i16),
}
impl Foo {
fn bar(&mut self, y: u32) -> (u32, &mut Foo) {
match (*self, y) {
(Foo::Foo1(ref mut a), b) if (b == 5) => {
print!("is five");
*a = b + 42;
(b, self)
}
(Foo::Foo2(ref mut a), b) if (b == 5) => {
print!("is five");
*a = (b + 42) as i16;
(*a * b, self)
}
_ => {
print!("is not five!");
(y, self)
}
}
}
}
I feel like I would need a match arm such as the following, but it doesn't seem to be valid syntax:
(ref mut f # Foo::Foo1, b) if (b == 5) => {
print!("is five");
f.0 = b + 42;
(b, f)
}
error[E0532]: expected unit struct/variant or constant, found tuple variant `Foo::Foo1`
--> src/main.rs:24:30
|
24 | (ref mut f # Foo::Foo1, b) if (b == 5) => {
| ^^^^^^^^^ not a unit struct/variant or constant
No, this is not safe. You are attempting to introduce mutable aliasing inside the match arm. The mutable reference a points into the same value as self. It would be possible to change self (e.g. *self = Foo::Foo1(99)) which would then invalidate a, so this code is disallowed.
Instead, mutably reborrow self in the match statement and have it return the first value of the tuple. Since this value doesn't have a reference to self, you can then return self with the result of the match:
enum Foo {
Foo1(u32),
Foo2(u32), // changed so I don't have to figure out what casting you meant
}
impl Foo {
fn bar(&mut self, y: u32) -> (u32, &mut Foo) {
let next = match (&mut *self, y) {
(Foo::Foo1(a), b # 5) => {
*a = b + 42;
b
}
(Foo::Foo2(a), b # 5) => {
*a = b + 42;
*a * b
}
_ => y,
};
(next, self)
}
}
However, returning self like this is rather pointless here. The caller already has a &mut Foo, so you don't need to "give it back". This allows simplifying to:
impl Foo {
fn bar(&mut self, y: u32) -> u32 {
match (self, y) {
(Foo::Foo1(a), b # 5) => {
*a = b + 42;
b
}
(Foo::Foo2(a), b # 5) => {
*a = b + 42;
*a * b
}
_ => y,
}
}
}
I would still say it is a safe operation, although the compiler may not be able to understand that
With non-lexical lifetimes, the borrow checker becomes more intelligent. Your original code with an added explicit reborrow compiles:
#![feature(nll)]
enum Foo {
Foo1(u32),
Foo2(u32), // changed so I don't have to figure out what casting you meant
}
impl Foo {
fn bar(&mut self, y: u32) -> (u32, &mut Foo) {
match (&mut *self, y) {
(Foo::Foo1(a), b # 5) => {
*a = b + 42;
(b, self)
}
(Foo::Foo2(a), b # 5) => {
*a = b + 42;
(*a * b, self)
}
_ => (y, self),
}
}
}
See also:
Why does matching on a tuple of dereferenced references not work while dereferencing non-tuples does?
What is the syntax to match on a reference to an enum?
How can I use match on a pair of borrowed values without copying them?
Is there any difference between matching on a reference to a pattern or a dereferenced value?

Trait for enums convertible to isize

I have several enums. Each of them has numeric tags and can be converted to integer (isize). I have a trait that all of them define. But instances of that trait are not convertible to integer any more, because the compiler can't prove all implementations are actually parameterless enums.
Is there any trait that would mean a type is a parameterless enum or that it is convertible to integer (by any means) that I could use as constraint in the generics that need to get the numeric value?
To be more specific, here is a code-sample (also on play):
enum Foo {
a = 1,
b = 2,
c = 3,
}
enum Bar {
d = 4,
e = 5,
f = 6,
}
trait Process : std::marker::MarkerTrait {
fn process(v: isize) -> String;
}
impl Process for Foo {
fn process(v: isize) -> String { format!("foo{}", v) }
}
impl Process for Bar {
fn process(v: isize) -> String { format!("bar{}", v) }
}
// just for sake of argument; something more complex in reality, of course
fn extern_call(v: isize) -> isize { 2 * v + 211 }
fn process<T: Process>(v: T) -> String {
T::process(extern_call(v as isize))
}
fn main() {
println!("a: {}", process(Foo::a));
}
Obviously the problem is in process, v as isize fails to compile with
error: non-scalar cast: T as isize
So I would like to do something like
process<T: Process + scalar>
or
process<T: Process + ToPrimitive>
or something that would tell the compiler that only types that can be cast to isize are permitted, but I don't know what that could be.
Add a method to your trait that coerces it to a isize. Then the compiler can verify that the objects meet the required criteria:
enum Foo {
A = 1,
B = 2,
C = 3,
}
enum Bar {
D = 4,
E = 5,
F = 6,
}
trait Process: {
fn as_isize(self) -> isize;
fn process(v: isize) -> String;
}
impl Process for Foo {
fn as_isize(self) -> isize { self as isize }
fn process(v: isize) -> String { format!("foo{}", v) }
}
impl Process for Bar {
fn as_isize(self) -> isize { self as isize }
fn process(v: isize) -> String { format!("bar{}", v) }
}
// just for sake of argument; something more complex in reality, of course
fn extern_call(v: isize) -> isize { 2 * v + 211 }
fn process<T: Process>(v: T) -> String {
T::process(extern_call(v.as_isize()))
}
fn main() {
println!("a: {}", process(Foo::A));
}
FYI, MarkerTrait isn't needed here (and is un-idiomatic):
MarkerTrait is intended to be used as the supertrait for traits that don't have any methods

Resources