Rust type parameter with HRTB

Viewed 184

I have the following type

struct Test<Args> {
  // ...
  phantom: PhantomData<Args>,
}

that needs to store, alongside it's contents, a type parameter for determining arguments to a function to be passed on later.

I use it as follows:

#![feature(unboxed_closures, fn_traits)]
impl<Args> Test<Args> {
    fn update<F>(&mut self, args: Args, f: F)
    where
         F: FnOnce<Args>
    {
       //...
       let value = f.call_once(args);
       // ...
    }
}

Unfortunately I cannot use it for borrowed values. I would need to be able to do something like

type MyTest = Test<for<'a> (&'a u32, &'a u32)>;
let mut t: MyTest = ...;
t.update((&5, &8), |lhs, rhs| lhs + rhs);

But Test<for<'a> (&'a u32, &'a u32)> is not valid syntax.

My workaround currently is to modify Test to instead take in an fn type and then restrict it alongside another type at the call site as follows:

#![feature(unboxed_closures, fn_traits)]
struct Test<F> {
  // ...
  phantom: PhantomData<F>,
}

impl<F> Test<F> {
    fn update<Args, F2>(&mut self, args: Args, f: F2)
    where
         F: FnOnce<Args>,
         F2: FnOnce<Args>,
    {
       //...
       let value = f.call_once(args);
       // ...
    }
}

type MyTest = Test<for<'a> fn(&'a u32, &'a u32)>;
let mut t: MyTest = ...;
t.update((&5, &8), |lhs, rhs| lhs + rhs);

But this is relatively ugly and requires changing the public interface. I'm also not sure if there's any issues with fn being contra-variant in it's arguments.

Is there any better way to achieve this while keeping the original signature (or close to it)?

I don't want to use the call-site F itself in the type generics, as I need it to be generic in the method and not the type, as it can be a closure with borrowed values while the type may be static.

1 Answers

The way to express the MyTest type alias is like so:

type MyTest<'a> = Test<(&'a u32, &'a u32)>;
Related