Generic closure arguments and HRTBs


#1

Consider this code:

fn add_closure<T>(v: &mut Vec<Box<FnMut(T)>>) {
    v.push(Box::new(move |_| {
    }));
}

fn main() {
    let mut v1 = Vec::<Box<FnMut(f32)>>::new();
    let mut v2 = Vec::<Box<FnMut(&i32)>>::new();
    
    // this works
    add_closure(&mut v1);
    
    // this doesn't work
    add_closure(&mut v2);
}

I realize this is because Box<FnMut(&i32)> desugars to Box<for<'r> FnMut(&'r i32)>. Is it possible to modify the signature of add_closure() such that this will compile?


#2

No. You need a function which accepts type constructor T

fn add_closure<T>(v: &mut Vec<Box<for<'a> FnMut(T<'a>)>>) //not a valid Rust code

Rust doesn’t (yet?) allow type constructors in generics.

For now you’ll need to use separate version of add_closure:

fn add_closure_ref<'a, T>(v: &mut Vec<Box<for<'b> FnMut(&'b T) + 'a>>)

Note that T effectively has 'static lifetime bound, as it has to outlive lifetime 'b for every possible 'b.


#3

I see there is an approved RFC for adding type constructors as associated types:

It’s not clear to me if my issue will be resolved as part of the work for associated type constructors. Is there a more relevant RFC?


#4

Good question. My reading of it is it does not help/apply here. It seems what’s needed is an enhancement to HRTB that allows using generic type constructors there, as @red75prime mentioned, whereas the RFC is focused on associated type constructors.

Maybe @withoutboats knows for sure?


#5

Here’s the closest I could get with today’s Rust. Unfortunately, this is rather buggy and also causes Rust to crash, but in principle it should work?

trait Lf<'a> { type Type; }

struct F32;
impl<'a> Lf<'a> for F32 { type Type = f32; }

struct RefI32;
impl<'a> Lf<'a> for RefI32 { type Type = &'a i32; }

fn add_closure<T: for<'b> Lf<'b> + 'static>(v: &mut Vec<Box<for<'a> FnMut(<T as Lf<'a>>::Type)>>) {
    v.push(Box::new(move |_| {
    }));
}

fn main() {
    // bug? compiler can't seem to deduce <F32 as Lf<'a>>::Type == f32 nor <RefI32 as Lf<'a>>::Type == &'a i32
    let mut v1 = Vec::<Box<for<'a> FnMut(<F32 as Lf<'a>>::Type)>>::new();
    let mut v2 = Vec::<Box<for<'a> FnMut(<RefI32 as Lf<'a>>::Type)>>::new();
    add_closure::<F32>(&mut v1);
    add_closure::<RefI32>(&mut v2);
}