With the recent stabilization of GATs, I wanted to play around with those. It so happens that I could use an abstraction over reference counted pointers.
I eventually came up with an implementation that would do the job in most cases, but I'm hitting problems with wrapping closures. This led me in a fool's errant, trying many different approaches, to no avail.
I'll use an slightly modified implementation I found on reddit in order to showcase my issue, I'll use it as it is shorter, if slightly less ergonomic.
All code is inside a test case.
First I define the abstraction:
use std::ops::Deref;
use std::rc::Rc;
// define generic pointers, only changed reddit's version by adding ?Sized
trait PointerFamily {
type Pointer<T: ?Sized>: Deref<Target = T>; // GAT here
fn new<T>(value: T) -> Self::Pointer<T>;
}
// only implement for Rc
struct RcFamily;
impl PointerFamily for RcFamily {
type Pointer<T: ?Sized> = Rc<T>;
fn new<T>(value: T) -> Self::Pointer<T> {
Rc::new(value)
}
}
It works for regular types, I'll not show the tests for that.
Time to introduce closures, I first make a test with a regular old Rc, That's what I want to emulate.
Rust_analyzer is providing the types, which I copied here.
// test naked rc with a closure
struct Bar(Rc<dyn Fn(i32) -> i32>);
impl Bar {
fn wrap<T: Fn(i32) -> i32 + 'static>(t: T) -> Self {
Self(Rc::new(t))
}
}
let _a = Bar::wrap(|a| 1 + a).0; // _a: Rc<dyn Fn(i32) -> i32>
Let's see how this fares with our traits:
// test generic pointer with a closure
let _a = RcFamily::new(|a: i32| -> i32 { a + 1 }); // _a: Rc<|i32| -> i32> ... no good
let a: Rc<dyn Fn(i32) -> i32> = RcFamily::new(|a| a + 1); // fixed by coercing to dyn Fn
let b: <RcFamily as PointerFamily>::Pointer<dyn Fn(i32) -> i32> = RcFamily::new(|a| a + 1); // do it generically
let _v = vec![a, b]; // Same type
I have to specify the result type to be a dyn Fn
for variable a
, For variable b
I try to be a bit more generic, but I still have to make use of the concrete RcFamilly
type.
Now I try to do this from a function, and that's where i have an issue :
// do it in a generic struct
struct Baz<P: PointerFamily>(P::Pointer<dyn Fn(i32) -> i32>);
impl<P: PointerFamily> Baz<P> {
fn new(t: P::Pointer<dyn Fn(i32) -> i32>) -> Self {
Self(t)
}
fn wrap<T: Fn(i32) -> i32 + 'static>(t: T) -> Self {
// error[E0308]: mismatched types
// --> src\lib.rs:149:76
// |
// 148 | fn wrap<T: Fn(i32) -> i32 + 'static>(t: T) -> Self {
// | - this type parameter
// 149 | let p: <P as PointerFamily>::Pointer<dyn Fn(i32) -> i32> = P::new(t);
// | ------------------------------------------------- ^^^^^^^^^ expected trait object `dyn Fn`, found type parameter `T`
// | |
// | expected due to this
// |
// = note: expected associated type `<P as PointerFamily>::Pointer<dyn Fn(i32) -> i32>`
// found associated type `<P as PointerFamily>::Pointer<T>`
// = help: type parameters must be constrained to match other types
// = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
// let p = P::new(t);
// let p: <P as PointerFamily>::Pointer<dyn Fn(i32) -> i32> = P::new(t);
// Self(p)
todo!()
}
}
type RcBaz = Baz<RcFamily>;
type Fam = RcFamily;
let _a = RcBaz::new(Fam::new(|a| a + 2)).0; // _a: Rc<dyn Fn(i32) -> i32>
The new
function works alright, but can't find a way to do the same coercing I did earlier. It looks like my types are maybe too general, or the type inference can't do it, or most likely I'm just not knowledgeable enough to find the correct incantation.
So there it is, how can I enclose a dyn Fn
in my generic pointer type from a function ? How to do the coercion ?