Explain why functions with no receiver are non-dispatchable?

From https://quinedot.github.io/rust-learning/dyn-trait-impls.html:

Why is this a problem? Couldn't the erased type's vtable for Trait have a pointer to its implementation of no_receiver()? The function pointed to by the vtable shouldn't have to have a receiver, right?

How would you call it? The &self param is a fat pointer that contains the vtable pointer, so you need it to get the vtable.

I was thinking something like

let ti = TraitImplementor;
let t: &dyn Trait = &ti;
ti.no_receiver();

But I suppose that means dyn Trait has a function no_receiver() which takes itself as the receiver... which doesn't match the signature defined in Trait.

Right. Maybe there could be new syntax to name the type for the vtable pointer, and make calls with it. I'm not sure how practical that would be.

1 Like

And this mismatch would mean that dyn Trait doesn’t implement Trait, which makes it useless for many cases. In particular, the function

fn trait_user<T: ?Sized + Trait>() {
    println!("{}", T::no_receiver());
}

is valid, but if T = dyn Trait, there is no place to get a vtable through which the no_receiver function can be called. In principle, Rust could support a way to use this type anyway in the case where you do have a vtable, but since you couldn’t use that dyn Trait as Trait at all otherwise, it’d be quite a new thing.

2 Likes

I don't have the time to go detail on this... But I actually think forcing dyn Trait implementing Trait is a design mistake. At least I think we can change the rule to dyn Trait impls Trait if and only if Trait is dyn-compatible, and permit dyn Trait for all traits.

2 Likes

It's not even complete already, but I agree it should be relaxed in some way (along with some other fix-ups).

1 Like