Recursive trait defaults, communicating intent


#1

consider this example: the intent is “foo and bar can be implemented by default in terms of each other, but the implementor must supply at least one”.
Would it be worth making the language warn about this, or would it get caught as a matter of course (non-terminating recursive functions)

// mutually recursive defaults
// intent is actually that you implement one or the other.
trait Foo {
    fn foo(&self) { self.bar();}
    fn bar(&self) { self.foo();}
}

struct X{}
impl Foo for X {
}

fn main(){
    let x:X=X{};
    x.foo();
}

#2

I think the compiler should probably emit an infinite recursion warning for something which does infinite recursion using a trampoline (like in your example). Plus there’s no reason why that lint couldn’t be applied to any function or method in your crate… It’s gonna be expensive though.

What about adding an attribute on the methods? I’m imagining something like this:

trait Foo {
    #[must_implement_this_or(Foo::bar)]
    fn foo(&self) { self.bar();}
    #[must_implement_this_or(Foo::foo)]
    fn bar(&self) { self.foo();}
}

Can you think of a couple real-world examples where something like this would be useful?


#3

I was about to make a suggestion for the allocator interface along the lines of this:-

trait Alloc { 
    // Must implement at least one of these:-
    unsafe fn alloc_hinted(sz:usize, align:usize,  useage_hints:u32)->*mut u8 {
         alloc(sz, align)   // default - ignores hints.
    }
    unsafe fn alloc(sz:usize, align:usize)->*mut u8 {
         alloc_hinted(sz,align, 0u32) // default hints =0
    }
    ...
}

… adding a hint-bits interface to the allocator, with transparent default. An allocator which does use the hints can just implement the hinted version, and all existing use passes the default values. An allocator which doesn’t use the hints can ignore them.

I realised this would have the unfortunate effect that: instead of the compiler telling you about unimplemented functions, it would crash when they’re called…

( ‘hints’ would be something like … bit 0 = thread local (default, don’t know); bit 1=‘definitely shared between threads’ (default, don’t know) bit 2 = ‘always use free-list’ ; bit3 = ‘very short lifetime hint’ (e.g. per-frame temporaries) ’ bit4 = ‘long lifetime’ (e.g. level load data) etc… )


#4

It could be nice to be able to mark two traits as mutiably exclusive. The following doesn’t compile right now, but perhaps it will in the future.

trait FooOrBar {
    fn foo_or_bar(&self);
}

trait Foo {
    fn foo(&self);
}

impl<T: Foo> FooOrBar for T {
    fn foo_or_bar(&self) {
        self.foo();
    }
}

trait Bar {
    fn bar(&self);
}

impl<T: Bar> FooOrBar for T {
    fn foo_or_bar(&self) {
        self.bar();
    }
}