Deref coercion works well for plain references. I want to take advantage of that coercion inside a newtype, but I can't seem to figure out how.
(See the end of this post for a playground link)
Let's say we have these types:
struct Foo;
struct Bar(Foo);
struct Quux(Bar);
struct Waldo(Quux);
In all these examples, I'll be trying to transitively coerce a &Waldo
all the way down to a &Foo
, using these impls:
impl Deref for Waldo { type Target = Quux; }
impl Deref for Quux { type Target = Bar; }
impl Deref for Bar { type Target = Foo; }
Coercing a plain reference works fine:
fn takes_a_foo(_: &Foo) {}
pub fn test() {
let waldo = Waldo(Quux(Bar(Foo)));
takes_a_foo(&waldo);
}
Now let's say we want to throw a newtype into the mix. Is there anything I can do to keep the same level of ergonomics?
#[repr(transparent)]
#[derive(Copy, Clone)]
struct RefWrapper<'a, T>(&'a T);
fn takes_a_wrapped_foo(_: RefWrapper<'_, Foo>) {}
pub fn wrapped_test() {
let waldo = Waldo(Quux(Bar(Foo)));
takes_a_wrapped_foo(RefWrapper(&waldo)); // <-- ???
}
Here is my closest attempt:
impl<'a, T> RefWrapper<'a, T> {
fn coerce<Target>(self) -> RefWrapper<'a, Target> where T: Deref<Target = Target> {
RefWrapper(self.0)
}
}
fn takes_a_wrapped_foo(_: RefWrapper<'_, Foo>) {}
pub fn wrapped_test() {
let waldo = Waldo(Quux(Bar(Foo)));
takes_a_wrapped_foo(RefWrapper(&waldo).coerce().coerce().coerce()); // :(
}
Note the three calls to coerce()
. I'm fine with one explicit call, but having to count up the total number of coercions needed at every call site is a bit silly. I think the way to improve on this is to take advantage of coercion transitivity somehow (instead of writing out the where T: Deref
bound by hand), but I'm not sure where to go from here.
Here's the full code in the playground. Can anyone point me in the right direction?