pre-RFC: final trait methods

In the body of a trait definition, allow final as a function qualifier. final appears before any other qualifier; the full qualifier order is subsequently final const async unsafe extern "ABI" fn.

Final functions must have a default method body. Impl blocks for the trait must not provide a definition for any final functions.

Dyn compatible final methods are put into the trait's dyn vtable and dispatched virtually like with any other dyn compatible trait methods. This behaves similar to a supertrait which blanket implements methods for all sized implementers of the primary trait. Self: Sized does not hold within the method body, as the impl may be used for other unsized kinds, like slices.

Dyn incompatible final methods are not put into the trait's dyn vtable; this is fundamentally impossible. However, they still are callable; a polymorphic version of the method is used for dyn Trait, unlike other dyn compatible methods, which polymorphically dispatch to the monomorphic implementation.

Final functions which aren't methods (don't take self in some form as the first parameter) are allowed, but make the trait dyn incompatible. Final functions called on a known type may be monomorphic or may be compiled polymorphically, as the compiler's detail.

7 Likes

When I was writing this up, I was hoping it would simplify the impl trick for fn as_dyn(&self) -> &dyn Trait[1], but unfortunately the needed Self: Sized bound for unsizing to work can't be made to hold in final methods, since the impl needs to work for slice types as well.

Despite that, I still think final methods can be useful enough to hold their weight; for marking methods like Iterator::map which can't be usefully implemented other than the default method body, or for marking methods that unsafe wants to rely on the impl of without making a separate trait, making them free functions, or marking the primary trait unsafe just to say "don't override these default method impls."


  1. pub trait Trait: AsDynTrait { /* ... */ }
    
    pub trait AsDynTrait {
        fn as_dyn_trait(&self) -> &(dyn Trait + '_);
    }
    
    impl<T: Trait /* + Sized */> AsDynTrait for T {
        fn as_dyn_trait(&self) -> &(dyn Trait + '_) { self }
    }
    

    This allows you to go from impl Trait to &dyn Trait without stacking further indirection on top of instantiations which already use dyn Trait. And if you put as_dyn_trait on Trait, delegating to AsDynTrait as an implementation detail, you can avoid stacking indirection for &dyn Trait as well (by overriding that impl… also kinda ruining the hope to get this behavior for free from final fn. ↩︎

4 Likes

Having 'final' methods in their own block sounds the best (impl Iterator { /* final methods */ }) and having them be shown separately in rustdoc side bar after required and provided methods would be nice.

1 Like