How to pass any amount of arguments in closure?

Hi, I know that RFC of variadic generic in rust is still on-going, but how could we pass uncertain number of arguments?
let's say I have a field f: FnOnce() -> T, I want f to take any possible arguments, maybe f(1), maybe f(1, 2), how to do it?
Thanks in advance

The short answer is that you can't.

In your example you could take a &[i32].
Or something like serde_json::Value that can be inspected to find out what it is. If you know exactly what type you are expecting, downcasting Any will work.

The main question is, what do you want to do with these arguments? The "how?" in such cases fully depends on "why?", as there might be multiple possible ways to do this, with different pros and cons.

actually I want to implement a very simple version of partial function in rust, so the function could be any kind of function with different numbers of arguments

You can't do this for any number of arguments, but it's possible if you have a specific function signature in mind.

fn partially_apply(
  n: u32, 
  func: impl Fn(u32, &str, Vec<u8>) -> usize,
) -> impl Fn(&str, Vec<u8>) -> usize {
  move |s: &str, v: Vec<u8>| func(n, s, v)
}

This is kinda awkward, though. So there's a good chance you won't be able to reuse the same partial application and currying tricks you get with normal functional languages.

so I try to copy the way axum did to handler, but it came out with non-sized problem, the link is:
Rust Playground (rust-lang.org)

Well, there are two obvious reasons:

  • Axum implements Handler for generic parameter, not for dyn Trait.
  • Axum doesn't try to store dyn Handler directly anywhere - it must be behind some pointer (most probably Box).

well the first one I think it's the same, axum impl for dyn trait also, the second, I didn't know know about that

Could you show the implementation you're talking about? I've linked to the (simplified) impl<F: FnOnce> Handler for F, there's no dyn FnOnce here.

yes but in where it's a Fn right? isn't where would put a dyn before it?

In principle it's possible to write an Args trait:

enum ArgError {
    IndexOutOfBounds {
        index: usize, 
        num_args: usize
    } 
    // any other applicable variants here
} 

trait Args {
  fn get(&self, index: usize) -> Result<&dyn Any, ArgError>;
}

You could then implement that trait for tuples of various lengths.
Of course whether that will work or not depends on your use case, and you'll have to downcast the arg returned by get() in order to get any use out of it, so it's not exactly free at runtime.

Hi thank you for replying! I solved the above problem but I am a noob to macro, how can I expand a vec in a macro and pass the values of the vec to a function? like I have a vec[1, 2, 3] and expand it to f(1, 2, 3)?

It's impossible, since macros can't generate different code depending on runtime values.

You are right, I probably need unstable feature to call the function

Can you provide an example of where you need partial function application?

It might be that we've gone down the rabbit-hole of variadic generics and implementing traits on tuples when actually there might be a much nicer solution if we take a step back.

On the surface, this sounds quite similar to the "decorator pattern".

trait Foo {
  fn bar(&self) -> usize;
}

struct Concrete;
impl Foo for Concrete { fn bar(&self) -> usize { 42 } }

struct AddOneDecorator<F>(F);

impl<F: Foo> Foo for AddOneDecorator<F> {
  fn bar(&self) -> usize {
    let initial_value = self.0.bar();
    initial_value + 1
  }
}

You shouldn't need to reach for nightly for something like this. I'm sure there are alternate solutions or ways of phrasing the problem.

Hi it's all for learning purpose, I am learning rust and haskell at the same time, so I want to implement something that can be done easily in haskell, I made something like this: Rust Playground (rust-lang.org), but I met another problem, trait object can not downcast back to its type in runtime, could you help me?

Check out the std::any module.

I'm sure you already know this but keep in mind that Rust and Haskell have different values and opinions, so something that may be easy or idiomatic in one language may be awkward or downright impossible in another (e.g. Rust can't generalise over different classes of monads, Haskell doesn't let you use pointers and requires a GC).

I'm not saying "don't do that", it's actually really beneficial to know the strengths and limitations of each language, but just be aware that there may be points of friction when you try to implement a certain pattern from language A in language B. You might also get some funny looks or people asking why you are using a really complex solution when there is a much simpler, idiomatic way to do it.

4 Likes

Hi I checked Any before and it's not what I want, because I do not know the runtime type either, I did found a library implement partial application function but it's all macros and I can hardly understand it, thanks for the comment!

This should be possible. Maybe if you have an example of the code you'd like to write we can figure it out in a separate thread?

Hi Michael, I am sorry I didn't save the code, it's all for practice, and someone else already did a more elegant way than I did so no reason to keep it ha ha

1 Like