Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to convince Rust that a particular associated type is the same as a concrete type?

Given the following traits and implementations:

trait Wrapper<Type> {
    type Inner : Wrapped<Type>;
    fn bind<B, F>(self, f: F) -> <B as Wrapped<Type>>::Outer
        where B: Wrapped<Type>, F: Fn(Self::Inner) -> <B as Wrapped<Type>>::Outer;
}

trait Wrapped<Type> {
    type Outer : Wrapper<Type>;
}

struct Opt;

impl<T> Wrapped<Opt> for T {
    type Outer = Option<T>;
}

impl<T> Wrapper<Opt> for Option<T> {
    type Inner = T;
    fn bind<B, F>(self, f: F) -> <B as Wrapped<Opt>>::Outer
        where B: Wrapped<Opt>, F: Fn(Self::Inner) -> <B as Wrapped<Opt>>::Outer {
        match self {
            Some(a) => f(a),
            None => None,  // *** fails to compile
        }
    }
}

It's obvious to a human that the type <B as Wrapped<Opt>>::Outer must always be Option<B>, but rustc can't seem to figure this out:

src/main.rs:47:21: 47:25 error: mismatched types:
 expected `<B as Wrapped<Opt>>::Outer`,
    found `core::option::Option<_>`
(expected associated type,
    found enum `core::option::Option`) [E0308]
src/main.rs:47             None => None,
                                   ^~~~

Is there any way to convince it that this is safe? I'd even settle for an unsafe solution, but mem::transmute is also not allowed since it can't prove that the types are the same size and aligned (even though there's only one real type involved and not even any newtype wrappers that could mess up alignment).

like image 771
Steve Avatar asked Dec 01 '15 10:12

Steve


1 Answers

This is most likely a bug (either this one or that one, I'm not sure). Until it's fixed, I can only think of doing some custom unsafe trickery. Here's a transmute function that does the size equality checks at runtime.

unsafe fn runtime_transmute<T, U>(t: T) -> U {
    assert_eq!(std::mem::size_of::<T>(), std::mem::size_of::<U>());
    std::ptr::read(&t as *const _ as *const _)
}

now you can replace your None value with

unsafe { runtime_transmute(None::<T>) }
like image 140
oli_obk Avatar answered Nov 15 '22 08:11

oli_obk