Is it possible to put functions of the same return type but of varying parameters into a list?

Rust Playground

I'm trying to create a list of linux commands that I then execute if legal.
And I want to reuse most of those commands if configured differently.

What I'm thinking of is thus putting a bunch of functions in a Vector,
which I will one by one,

  1. First call in order to confirm it's legality and then return the optional command.
  2. Then run the command.

Is this possible?

[edit]

@Jofas Solution was the solution I was looking for, but conqp's solution might actually be even better/more idiomatic for my use case.

You could do this with dynamic dispatch:

Or if you don't need to support closures that capture anything, you could use function pointers as you tried in your OP.

2 Likes

Ah, these are the things I'm not familiar with in Rust but have seen coming across.

But if I understand correctly from stackoverflow..

"dyn" allows you to store in a Box a mix of Apples and Oranges, because they all implement the same trait of Fruit

..and..

The dyn keyword is used to indicate that a type is a trait object.

..then how does one create function pointers with varying parameters in a vector list,
if fn() is not a trait and thus can't be used with dyn, as far as I can tell?

I'm not sure why I need closures, but I do need varying parameters.

The real question is how do you plan to work with these things once they’re removed from the list? In particular, how will you make sure that you’re providing the right parameters to each one?

In practice, you’ll need to define some kind of common API that applies to all of the functions and lets you do whatever you need to do once they’ve been added to the list. @jofas’ solution does this by packing up the relevant parameters into a closure with the function so that they travel together. If that doesn’t meet your needs, you can define whatever API you need with a combination of traits and wrapper types, but the specifics will depend on details you haven’t given us yet.

2 Likes

It meets my needs. I'm trying to understand what he's saying when he's saying:

you could use function pointers as you tried in your OP.

It doesn't seem possible to me, so I'm asking.

Well, mostly I missed the varying parameters requirement when I read your OP. But you could use function pointers if you abstract the different parameter types that your functions expect into an enum, for example. Don't know if that is applicable in your real use-case.

My real use-case is only slightly more complicated in so far that I try to use vectors of functions.
No wait that's not right....hold on...

What I want to use are sometimes functions that return
lists of commands and bunching up commands to one text.
It was easy when there were no boxes and dynamic functions to think of.

1 Like

If you can coerce to a dyn Fn(Args) -> Out and have no captures - so you could be written as a standalone function - then you can also coerce to fn(Args) -> Out. You can think of it as a special case if that helps.

Neither approach gives you actually varying input arguments in a homogenous type.

2 Likes

What I want to use are sometimes functions that return
lists of commands and bunching up commands to one text.
It was easy when there were no boxes and dynamic functions to think of.

I got it..

use duct::Expression;
...
pub fn get_command_list<'a>(&'a self) -> Vec<(&str, Vec<Box<dyn Fn() -> Vec<Expression> + 'a>>)> {
...
1 Like

Another option is to use a trait with a call(&self) -> CommonResultType method.
Every implementing struct can then store the parameters needed for the call, so that they are accessible through &self. Your sequence then can store dyn TraitWithCall.

3 Likes

In your specific example, you're not storing functions anywhere in the first place. getCommandList can have the signature

    pub fn get_command_list(&self) -> Vec<(&str, Option<Command>)>

No other changes are required; your code then compiles cleanly.

Other changes are required.
I need to be able to call the functions to return commands in place.

If mountpoint is already mounted during the installation process and I call mount,
the program quits on an error.
Since I mount and unmount certain filesystems multiple times,
this means that I can't do the check before I collect the commands in a vec.

I also unmount devices,
which means I will need a function that returns a
list of filesystem to mount and unmount.

A bit odd to turn a process like an addition into a struct,
but it looks a lot more readable.
And I can also use supertraits to distinguish single commands
from command series.

I'll try and see if I can use that instead.