是否可以在结构中扩展特征的默认方法实现?

fai*_*ace 12 oop inheritance default traits rust

在传统的面向对象语言(例如Java)中,可以通过从重写版本中的超类调用原始方法来"扩展"继承类中方法的功能,例如:

class A {
    public void method() {
        System.out.println("I am doing some serious stuff!");
    }
}

class B extends A {
    @Override
    public void method() {
        super.method(); // here we call the original version
        System.out.println("And I'm doing something more!");
    }
}
Run Code Online (Sandbox Code Playgroud)

如您所见,在Java中,我可以使用super关键字从超类中调用原始版本.我能够获得继承特征的等效行为,但在实现结构的特征时却没有.

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

trait Boo: Foo {
    fn method(&self) {
        // this is overriding the default implementation
        Foo::method(self);  // here, we successfully call the original
                            // this is tested to work properly
        println!("I am doing something more.");
    }
}

struct Bar;

impl Foo for Bar {
    fn method(&self) {
        // this is overriding the default implementation as well
        Foo::method(self);  // this apparently calls this overridden
                            // version, because it overflows the stack
        println!("Hey, I'm doing something entirely different!");
        println!("Actually, I never get to this point, 'cause I crash.");
    }
}

fn main() {
    let b = Bar;
    b.method();     // results in "thread '<main>' has overflowed its stack"
}
Run Code Online (Sandbox Code Playgroud)

因此,在继承traits的情况下,调用原始默认实现是没有问题的,但是,在实现结构时使用相同的语法表现出不同的行为.这是Rust内的一个问题吗?有办法解决吗?或者我只是错过了什么?

huo*_*uon 9

现在这是不可能的.

但是,RFC 1210:impl专业化包含使这种行为有效的各个方面,例如,这样的事情应该起作用:

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"); }
}
Run Code Online (Sandbox Code Playgroud)

super明确提到进行呼叫是对它的扩展,因此不一定会立即出现,但可能会在将来出现.

同时,通常使用的方法是为默认行为定义一个单独的函数,并在默认方法中调用它,然后用户可以super::...通过直接调用该函数来模拟调用:

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");
    }
}
Run Code Online (Sandbox Code Playgroud)

也就是说,Rust更喜欢构图而不是继承:在Java中运行良好的设计不能也不应该被强制一对一地转换为Rust.

    Foo::method(self);  // this apparently calls this overridden
                        // version, because it overflows the stack
Run Code Online (Sandbox Code Playgroud)

合格的路径语法Trait::method(value)是糖,<Type as Trait>::method(value)因为Type它的类型value(或者,可能是解除引用一些次数后的类型).也就是说,它正在您发现的特定类型上调用方法.

  • 它不是你所期望的.你正在定义一个与`Foo :: method`不同的新方法`Boo :: method`.它仍然在调用类型的具体方法; 我猜你的测试代码没有覆盖`Foo`上的`method`,只在`Bar`上. (3认同)

She*_*ter 7

这是 Rust 中的问题吗?

不,这正在按预期工作

有办法解决吗?

您可以将该方法移至自由函数,然后直接调用它,一次从默认方法调用,一次从“重写”方法调用。

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();
}
Run Code Online (Sandbox Code Playgroud)

或者我只是错过了什么?

Rust 不是面向对象的语言, Rust 可能是面向对象的语言,但并非所有 OO 语言都是一样的。Rust 可能不太适合您期望的传统范例。

也就是说,特征在运行时不存在。仅当它们应用于结构并与结构一起使用时,才会生成可调用的代码。当您创建自己的方法实现时,它将取代默认实现;默认方法实现不存在。

很多时候,您的代码可以用不同的方式编写。也许真正共享的代码应该作为新结构上的方法提取,或者您可能为方法提供闭包以自定义行为。

  • @Shepmaster:“Rust 不是面向对象的语言。” 这是完全错误的。Rust 并不支持完整的“基于类”OOP,但它“绝对”是一种面向对象的语言。(为什么,连维基百科的文章都这么说!) (4认同)
  • @ChrisMorgan 公平点,我已经更新了。然而,我担心人们听到“OO”并将其翻译为“Java/C# 可以做什么”。 (3认同)

Sha*_*s M 6

实现此目的的另一种方法是将覆盖方法放在impl结构块中

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();
} 
Run Code Online (Sandbox Code Playgroud)

操场

  • 这不会做同样的事情。如果您从使用特征作为类型参数的实例调用方法 a(),它只会调用特征默认方法。游乐场链接:https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2018&amp;gist=d2aa407048d01522dbc48a748b34d5f8 (4认同)
  • 哇,我真的没想到会有这种行为。感谢您展示这一点。 (2认同)