Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamically select a function to call without intermediate variables

Tags:

rust

I'm trying to select a function to call depending on a condition. I want to store that function in a variable so that I can call it again later without carrying the condition around. Here's a working minimal example:

fn foo() {
    println! ("Foo");
}
fn bar() {
    println! ("Bar");
}

fn main() {
    let selector = 0;

    let foo: &Fn() = &foo;
    let bar: &Fn() = &bar;
    let test = match selector {
        0 => foo,
        _ => bar
    };
    test();
}

My question is: is it possible to get rid of the intermediate variables? I've tried simply removing them:

fn foo() {
    println! ("Foo");
}
fn bar() {
    println! ("Bar");
}

fn main() {
    let selector = 0;

    let test = match selector {
        0 => &foo as &Fn(),
        _ => &bar as &Fn()
    };
    test();
}

but then the borrow checker complains that the borrowed values are only valid until the end of the match (btw, why? the functions are 'static anyway so should be valid to the end of times). I've also tried making the 'static lifetime explicit by using &foo as &'static Fn() but that doesn't work either.

like image 809
Jmb Avatar asked Oct 01 '15 10:10

Jmb


1 Answers

The following works, if you only need to work with static functions and not closures:

fn foo() {
    println!("Foo");
}

fn bar() {
    println!("Bar");
}

fn main() {
    let selector = 0;

    let test: fn() = match selector {
        0 => foo,
        _ => bar
    };

    test();
}

(try on playground)

Here I've used function type instead of function trait.

The reason that the borrowed trait object doesn't work is probably the following. Any trait object is a fat pointer which consists of a pointer to some value and a pointer to a virtual table. When the trait object is created out of a closure, everything is clear - the value would be represented by the closure itself (internally being an instance of a structure containing all captured variables) and the virtual table would contain a pointer to the implementation of the corresponding Fn*() trait generated by the compiler whose body would be the closure body.

With functions, however, things are not so clear. There are no value to create a trait object from because the function itself should correspond to the implementation of Fn() trait. Therefore, rustc probably generates an empty structure and implements Fn() for it, and this implementation calls the static function directly (not actual Rust, but something close):

struct SomeGeneratedStructFoo;

impl Fn<()> for SomeGeneratedStructFoo {
    type Output = ();
    fn call(&self, args: ()) -> () {
        foo();
    }
}

Therefore, when a trait object is created out of fn foo(), a reference is taken in fact to a temporary value of type SomeGeneratedStructFoo. However, this value is created inside the match, and only a reference to it is returned from the match, thus this value does not live long enough, and that's what the error is about.

like image 76
Vladimir Matveev Avatar answered Sep 22 '22 08:09

Vladimir Matveev