Why isn't Into dyn compatible

So Yes I i'm aware that it has trouble writing a vtable if it requires self however we are converting one type into another known Sized type which confuses me because a Box<dyn Into> should have no trouble making a vtable as it contains known types and it doesn't have a refrence so we can figure out it's size. I'm just wondering and would like clarification.

For that to work, the signature of into() would have to be into(self: Box<Self>) rather than just into(self: Self). The reason why this affects dyn compatibility has to do with the calling convention being uniform at the ABI level, which is…not a very satisfying answer, but has its reasons.

You could probably argue impl <T, U> From<Box<T>> for U where U: From<T> should be provided in the standard library; unfortunately that would conflict with other implementations.

That said, if you need this, you can make your own. This instructs the compiler to do the necessary bridging between the two function signatures.

2 Likes

Object safety does not directly care about Box.

Instead ask yourself this question: can dyn Into<T> implement Into<T>? You end up with a function into taking a dyn Into<T> by value. Notice how the starting type now is no longer sized. This is not currently allowed, and hence cannot be valid right now.

Maybe in the future once we get something like owned references or DST function parameters it might become possible.

3 Likes

We're already committed to keeping unsized_fn_params around in some form/capacity due to stable Box<dyn FnOnce(..)> implementations.

That said, removing the supertrait bound is a breaking change. Though perhaps the breakage would be manageable.

Also of note, these both conflict with the existing blanket Into implementation.

impl<T: ?Sized + Into<U>, U> Into<U> for Box<T>
impl<U> Into<U> for Box<dyn Into<U> + '_ /* + any combo of auto traits */>

So relaxing the bound might not be as useful as one hopes.

3 Likes

Edit: Wait perhaps not, blanket impls are breaking. But perhaps these are still possible due to the existing implementation? There's a lot of fundamental involved and I have to run before thinking it through, but I'll leave this here in case someone has some feedback.

Edit 2: Yeah, anyone can impl From<Box<dyn FnOnce() -> LocalTy>> for LocalTy so the below idea is probably too breaking.

Original post

Followup thought: I believe we could have these today.

impl<T> From<Box<dyn '_ + FnOnce() -> T>> for T {
    fn from(bx: Box<dyn '_ + FnOnce() -> T>) -> Self {
        bx()
    }
}

impl<T, E> TryFrom<Box<dyn '_ + FnOnce() -> Result<T, E>>> for T {
    type Error = E;
    fn try_from(bx: Box<dyn '_ + FnOnce() -> Result<T, E>>) -> Result<Self, Self::Error> {
        bx()
    }
}

// And every auto-trait combo

Then

// `dyn FnOnce() -> T + 'u` can be a stand-in for `dyn Into<T> + 'u`
type Make<'u, T> = impl Into<T>;
#[define_opaque(Make)]
fn a_dab_of_indirection_will_do<'u, T, U: Into<T> + 'u>(u: U) -> Make<'u, T> {
    Box::new(|| u.into()) as Box<dyn FnOnce() -> _>
}