Custom pointer and unsized types

I need a smart pointer type that is basically a wrapper around Rc. Trying to make it work with unsized types I came across the following problem (Playground):

use std::rc::Rc;

struct Ptr<T: ?Sized>(Rc<T>);

impl<T: ?Sized> Ptr<T> {
    fn new(value: T) -> Self
    where
        T: Sized,
    {
        Self(Rc::new(value))
    }

    // Workaround for the lack of CoerceUnsized in stable rust.
    fn into_unsized<U>(self) -> Ptr<U>
    where
        U: ?Sized,
        Rc<T>: MyCoerceUnsized<U>,
    {
        Ptr(self.0.coerce())
    }
}

trait MyCoerceUnsized<U: ?Sized>: Sized {
    fn coerce(self) -> Rc<U>;
}

impl<A1, A2, R, F> MyCoerceUnsized<dyn Fn(A1, A2) -> R> for Rc<F>
where
    F: 'static + Fn(A1, A2) -> R,
{
    fn coerce(self) -> Rc<dyn Fn(A1, A2) -> R> {
        self
    }
}

fn tst1<F: 'static + Fn(&mut i32, &mut i32)>(f: F) -> Ptr<dyn Fn(&mut i32, &mut i32)> {
    // Error:
    // expected struct `Ptr<dyn for<'a, 'b> Fn(&'a mut i32, &'b mut i32)>`
    //    found struct `Ptr<dyn Fn(&mut i32, &mut i32)>`
    Ptr::new(f).into_unsized()
}

// This fails too.
fn tst2<F: 'static + for<'a, 'b> Fn(&'a mut i32, &'b mut i32)>(
    f: F,
) -> Ptr<dyn Fn(&mut i32, &mut i32)> {
    Ptr::new(f).into_unsized()
}

// And so does this.
fn tst3<F: 'static + for<'a, 'b> Fn(&'a mut i32, &'b mut i32)>(
    f: F,
) -> Ptr<dyn for<'a, 'b> Fn(&mut i32, &mut i32)> {
    Ptr::new(f).into_unsized()
}

// This works.
fn tst4<F: 'static + Fn(i32, i32)>(f: F) -> Ptr<dyn Fn(i32, i32)> {
    Ptr::new(f).into_unsized()
}

Am I missing something or is it a compiler bug?

in the type system, the type dyn Fn(A1, A2) is different from dyn for<'a, 'b> Fn(&'a mut A1, &'b mut A2), thus the blanket implementation does not apply for types with late bounded lifetimes.

on the other hand, if you only add the blanket implementation for the late bounded dyn Fn type:

impl<A1, A2, R, F> MyCoerceUnsized<dyn for <'a, 'b> Fn(&'a mut A1, &'b mut A2) -> R> for Rc<F>
where
	F: 'static + Fn(&mut A1, &mut A2) -> R,
{
	fn coerce(self) -> Rc<dyn for<'a, 'b> Fn(&'a mut A1, &'b mut A2) -> R> {
		self
	}
}

then tst1, tst2, and tst3 should compile, but tst4 stops to compile.

if you have both, then all compiles, but you will get a #[warn(coherence_leak_check] default lint.

playground

Ok, thanks for your help.

I thought having both Fn(A1,A2) and Fn(&mut A1, &mut A2) wouldn't be possible without specialization, but since it works I can use that.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.