Sounds magical: non-dispatchable functions actually can be dispatchable

To figure the quotation out, I try to find the tricks via the following code:

fn main() {
    let mut a = A;

    // let ref_a: &dyn NonDispatchable = &a;
    // ref_a.typed("");

    let ref_mut_a: &mut dyn NonDispatchable = &mut a;
    ref_mut_a.typed("hi");

    let box_a: Box<dyn NonDispatchable> = Box::new(a);
    box_a.typed("there");
}

// src: https://doc.rust-lang.org/reference/items/traits.html#object-safety
trait NonDispatchable {
    // Generics are not compatible with vtables:
    fn typed<T: std::fmt::Debug>(&self, t: T)
        where Self: Sized {
        println!("{t:?}");
    }
}

struct A;

impl NonDispatchable for A {}

// Still non-dispatchable:
// impl<T: NonDispatchable + ?Sized> NonDispatchable for &T {}

// Dispatchable:
impl<T: NonDispatchable + ?Sized> NonDispatchable for &mut T {}

// Dispatchable:
impl<T: NonDispatchable + ?Sized> NonDispatchable for Box<T> {}

Is it what the Reference: object-safety misses or badly states?

Or is this trick documented else where?

If you read accurately:

Dispatchable functions require

  • Does not have a where Self: Sized bound (receiver type of Self (i.e. self ) implies this).

Self: Sized makes this function available for concrete type only, hence trait satisfies object safety (I assume it also visible to compiler how to resolve function call in this context)

P.s. not sure if compiler should resolve calls via trait objects for this method though, maybe bug of optimization

Neverminded last line, compiler should be able to resolve method due to having specialized implementation for mutable reference and Box, making it resolved statically

You can print Self type and see for yourself:

&mut dyn playground::NonDispatchable:"hi"
alloc::boxed::Box<dyn playground::NonDispatchable>:"there"
1 Like

You are not really dispatching though. If you add follow code and rerun, it won't print A

impl NonDispatchable for A {
   fn typed<T: std::fmt::Debug>(&self, t: T)
    where
        Self: Sized,
    {
        println!("A");
    }
}

What you are calling is no different from

impl dyn NonDispatchable {
   fn typed2<T: std::fmt::Debug>(&self, t: T) {
        println!("{t:?}");
   }
}
2 Likes

Well, yes. Thank you for pointing that out.

Non-dispatchable functions can never be dispatchable.