[Retracted-Pre-RFC] Make `impl Trait {...}` more usable


#1

This is somewhat of a haphazard pre-RFC because it’s really just a collection of paper-cuts.

Motivation

  1. This would allow private trait helper methods.
  2. This would allow non-overridable methods on traits.
  3. This would allow (some) generic trait methods to be called on trait objects. Really, all the map, fold, etc. functions on Iterator should be implemented this way but it might be a bit late to change that.

Specifically motivated by: https://users.rust-lang.org/t/dynamic-dispatch-and-sized-trait/2918

Design (ish)

Basically, fix the following 4 comments (if possible).

trait Test {
    fn call_test(&self) {
        // 1. Complains about missing test method. This should (be made to) work.
        self.priv_test(0);
        // 2. Complains that Self isn't Sized. Is there any reason this can't work?
        (self as &Test).priv_test(0);
    }
}

// 3. I shouldn't need to specify this lifetime but, if I don't, `&something as &Test`
//    complains about `something` not having a static lifetime (because this defaults to
//    `impl Test + 'static`). As far as I can tell, this case isn't specified in any of
//    the object lifetime bounds RFCs.
impl<'a> Test + 'a {
    fn priv_test<A>(&self, a: A) { }
    pub fn pub_test<A>(&self, a: A) { }
}

struct MyStruct;

impl Test for MyStruct { }

fn main() {
    let s = MyStruct;
    // 4. It should be possible to call this method directly.
    s.pub_test(0);
}

Drawbacks

Dynamic dispatch. The trait methods rely on dynamic dispatch so calls back into the trait would be somewhat slower in a naive implementation. However, it should be possible to optimize this when the receiver’s type is actually known.


#2

For (2), see http://stackoverflow.com/questions/28632968/why-doesnt-rust-support-trait-object-upcasting .

(3) is consistent with the way trait types are treated in every other context; not sure it’s really worth changing.

(1) and (4) are essentially the same… method lookup simply doesn’t work that way at the moment. Not really familiar enough with that part of the compiler to say more.


#3

Maybe I’m mistaken but I’m pretty sure it’s not the same thing. In this case, the concrete type is known at compile time (self is not a trait object; remember, this is a default method). That is, a vtable is used to find the method but the method itself is monomorphized once per receiver. However, I this won’t work anyways because, if the underlying self type isn’t sized, the pointer will already be fat so it won’t be able to fit a vtable.

Not really. &something as &Trait defaults to &something as &'a (Trait + 'a). Furthermore, if I write impl<T> Test for T {}, T: 'static isn’t assumed.

I know; I’m proposing that this be changed. Given that I only want methods implemented on the trait object to work (not traits implemented on the trait object), I’ve edited the post.


#4

That isn’t precisely correct… consider:

#![crate_type="lib"]
trait Test {
    fn call_test(&self) -> &Test { self }
}
pub trait A {}
impl Test for A {}
pub fn f(a: &A) -> &Test {
    a.call_test()
}

#5

Yeah, I just realized that. Really, I primarily care about generic functions on object safe traits. It’s still doable (handle the “can be a trait object” and “can’t be a trait object” cases separately) but it won’t be as elegant.