Allow <instance as Trait>::foo()


#1

Allow

let x = foo;
let y = <x as Foo>::bar();
let z: dyn Foo = &foo;
let w = <z as Foo>::bar();

This would need a new autotrait or something, RequiresObjectAsTraitSyntax, that is also implicitly disallowed if your function uses <Type as Foo> or another function that does so.

it’d be fine to do things like <Type as Bar> (where Bar doesn’t ROATS) and still accept dyn Foo, but anyway the point of this is to be backwards compatible and just allow more than we currently allow.


TypeFns, variadic generics, compile-time reflection, macro-like fns
#2

This specifically seems to require expanding the vtable to include static functions, which could be really useful for some cases.

I’m not sure what the new autotrait is for? The non-dynamic variant seems to just be compiler sugar (which I could definitely see being useful in a few cases to reduce accidentally changing one place when you need to change two, similar to using sizeof(value) instead of sizeof(struct foo) in C), while the dynamic variant would be just expanding the vtable; where does the autotrait come into those?


#3

I want these to work:

#[macro_use]
extern crate eventbus;

use eventbus::{Event, EventBus};

trait MyEvent : Event {
    fn get_i(&self) -> i32;
    fn set_i(&mut self, i32);
}
        
struct MyEventImpl {
    i: i32
}   

impl Event for MyEventImpl {
}
impl MyEvent for MyEventImpl {
    fn get_i(&self) -> i32 {
        self.i
    }
    fn set_i(&mut self, i: i32) {
        self.i = i;
    }
}

fn add_handler(e: &mut MyEvent) {
    /* adds 1 */
    let i = e.get_i();
    e.set_i(i+1);
}

fn no_handler(e: &mut MyEvent) {
    /* does nothing */
}

#[test]
fn test_basic_usage() {
    let event_bus = EventBus::new();
    //let handler_id = event_bus.register(add_handler, 0);
    register_hook!(&event_bus, 0, dyn MyEvent, add_handler);
    let mut event = MyEventImpl { i: 3 };
    assert_eq!(event.i, 3);
    post_event!(&event_bus, dyn MyEvent, &mut event);
    assert_eq!(event.i, 4);
    register_hook!(&event_bus, 1, dyn MyEvent, no_handler);
    post_event!(&event_bus, dyn MyEvent, &mut event);
    assert_eq!(event.i, 5);
    //event_bus.unregister(handler_id);
    post_event!(&event_bus, dyn MyEvent, &mut event);
    //assert_eq!(event.i, 5);
}

The autotrait is used for creating bounds, e.g.:

fn foo1<T: Bar + ?Sized>(x: &T) {
    <T as Bar>::baz(); // T gets an implicit bound `+ !ROATS`
}

fn foo2<T: Bar + ?Sized>(x: &T) {
    <x as Bar>::baz(); // T gets an implicit bound `+ ?ROATS`
}

(This is more useful with macros)


#4

But what do those bounds mean, those two functions would be identical after compiler desugaring of <*x as Bar> to <T as Bar> (I assume <x as Bar> is a typo since &T isn’t guaranteed to implement Bar in foo2) so I don’t see what the bounds are meant to change.


#5

it’s meant to use the vtable rather than using the type.

&dyn Bar has a vtable, unlike the original &BarImpl, and that’s the main difference between them. we just need to make the vtable’s static methods nameable and callable.


#6

Sure, in the case of dyn Trait it needs to use a vtable. For statically known types it doesn’t need to though, and since it’s trivial to distinguish the two cases I don’t see why you would still go through a vtable and give the optimizer more work.

Even if it always used a vtable, why does that need an extra bound? I would assume static methods would just be added to all vtables (since it’s dynamic and you can’t know whether they would be needed) and it would just always work.


#7

Because in foo1 there’s no vtable when T: &dyn Bar because that’s the type and not the value.

Note that I explicitly propose changing the semantics of <type>::method(), if type is derived from an object.


#8

Change in what sense? What else should the expression Foo::bar() ever do other than calling the method bar on (an object of) type Foo?


#9

It should use the vtable.