Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to assign a closure to a variable of type impl Fn()?

I was able to make this code work:

fn twice<T: Clone>(fst: impl Fn(T), snd: impl Fn(T)) -> impl Fn(T) {
    move |t| {
        fst(t.clone());
        snd(t)
    }
}

However, what I want is this (without boxing):

fn sub<T: Clone>(mut fst: impl Fn(T), snd: impl Fn(T)) {
    fst = move |t: T| {
        fst(t.clone());
        snd(t)
    };
}

Is there a way I can make the second piece of code work without boxing, using traits, type casting or any other method? Rust complains that the types do not match.

like image 709
Edgar Avatar asked Mar 04 '23 16:03

Edgar


1 Answers

This cannot be done without boxing. The reason is that the actual type of fst in the input is different than the type of the closure that you later overwrite it with. The only way to make them the same type is with a trait object.

The boxed version might look like this:

use std::mem;

fn sub<'a, T: Clone + 'a>(fst: &mut Box<dyn Fn(T) + 'a>, snd: impl Fn(T) + 'a) {
    // Replace the original fst with a dummy closure while the new closure is being
    // constructed, to avoid the reference being temporarily invalid
    let fst_orig = mem::replace(fst, Box::new(|_| {}));
    *fst = Box::new(move |t: T| {
        fst_orig(t.clone());
        snd(t)
    });
}


fn main() {
    let mut f1: Box<dyn Fn(i32)> = Box::new(|x| println!("f1: {}", x));
    let f2 = |x| println!("f2: {}", x);

    sub(&mut f1, f2);

    f1(42);
}

But I really am not sure why you would want to do this!

like image 166
Peter Hall Avatar answered Apr 13 '23 01:04

Peter Hall