Is it possible to extend a default method implementation of a trait in a struct?

你说的曾经没有我的故事 提交于 2019-11-27 22:59:22

This isn't possible directly now.

However, RFC 1210: impl specialization contains various aspects that will make this sort of behaviour work, for example, something like this should work:

trait Foo {
    fn method(&self) { println!("default implementation"); }
}
trait Bar: Foo { ... }

partial impl<T: Bar> Foo for T {
    default fn method(&self) { println!("Bar default"); }
}

Doing a super call is explicitly mentioned as an extension to it and so won't necessarily appear immediately, but may appear in future.

In the mean time, the approach generally used is to define a separate function for the default behaviour and call that in the default method, and then users can emulate the super::... call by just calling that function directly:

trait Foo {
    fn method(&self) { do_method(self) }
}

fn do_method<T: Foo>(_x: &T) {
    println!("default implementation");
}

impl Foo for Bar {
    fn method(&self) {
        do_method(self);
        println!("more");
    }
}

That said, Rust favours composition over inheritance: designs that work well in Java can't and shouldn't be forced 1-to-1 into Rust.

    Foo::method(self);  // this apparently calls this overridden
                        // version, because it overflows the stack

The qualified path syntax, Trait::method(value) is sugar for <Type as Trait>::method(value) where Type is the type of value (or, possibly, the type after dereferencing some number of times). That is, it's calling the method on the specific type as you found out.

Is this a problem within Rust?

No, this is working as intended

Is there a way around it?

You can move the method to a free function and then call it directly, once from the default method and once from the "overridden" method.

fn the_default() {
    println!("default implementation");
}

trait Foo {
    fn method(&self) {
        the_default()    
    }
}

struct Bar;

impl Foo for Bar {
    fn method(&self) {
        the_default();
        println!("Hey, I'm doing something entirely different!");
    }
}

fn main() {
    let b = Bar;
    b.method();
}

Or am I just missing something?

Rust is not an object-oriented language, Rust may be an object-oriented language, but not all OO languages are created the same. Rust may not quite fit into the traditional paradigms that you expect.

Namely, traits don't exist at run time. Only when they are applied to and used with a struct is code generated that is callable. When you create your own implementation of a method, that supersedes the default implementation; there's nowhere for the default method implementation to exist.

Often times, your code can be written in a different way. Perhaps the truly shared code should be extracted as a method on a new struct, or maybe you provide a closure to a method to customize the behavior.

Another way of accomplishing this would be to place the overriding method in impl block of struct

trait A {
    fn a(&self) {
        println!("trait default method");
    }
}

struct B;

impl B {
    fn a(&self) {
        println!("overridden method");
        // call default method here
        A::a(self);
    }
}

impl A for B {}

fn main() {
    let a = B;
    a.a();
} 

playground

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!