Rust中与C++的虚函数相对应的是什么?

6

我正尝试用Rust实现类似于C++中类的虚函数的功能。首先定义一个基础结构体,包含数据,然后保留一些未定义的函数,就像下面这个例子:

class A {
    int stuff;
public:
    virtual void foo(int a, int b) = 0;
    void function_that_calls_foo() { /*...*/ foo(1, 2); /*...*/ }
}

class B: public A { void foo(int a, int b) { /* ... */ } }

我曾尝试使用函数指针来实现,但没有成功。我可以使用带有A函数的特征,并在另一个类上实现A,但我将失去结构体的数据。在Rust中实现这种东西的最佳(最快?)方法是什么?

struct A {
    ...
}

impl A {
    fn function_that_calls_foo(&self) {
        ...
        self.foo(a, b);
        ...
    }
}

struct B {
    a: A;
}

impl B {
    fn xxx(&self) {
        a.function_that_calls_foo(1, 2);
    }

    fn foo(&self, a: i32, b: i32) {...}
}

4
你正在寻找trait objects - E net4
4
因此,Rust非常强调组合而非继承。 - Matthieu M.
16
这种类型的问题,“如何在Rust中实现类继承?”通常最好通过退一步,审视你尝试解决的实际问题(真正的问题,而不是用fooxxx作为函数名称的虚假问题),并查看你的新工具箱来解决。如果你试图直接将类继承翻译成Rust代码,你会找到一种使其运行的方法,但与更符合Rust惯例的代码相比,代码可能非常笨重。 - Dietrich Epp
1个回答

12

保持一些函数未定义

我正在添加隐含的 "并有一些函数调用该待定义函数"。

E_net4所说,使用trait

trait Foo {
    fn foo(&self, a: i32, b: i32) -> i32;

    fn function_that_calls_foo(&self) {
        println!("{}", self.foo(1, 2));
    }
}

然后,您可以为Base实现该特质:

struct Base {
    stuff: i32,
}

impl Foo for Base {
    fn foo(&self, a: i32, b: i32) -> i32 {
        self.stuff + a + b
    }
}

正如Matthieu M.所说,Rust没有继承,因此请使用组合:

struct Base {
    stuff: i32,
}

impl Base {
    fn reusable(&self) -> i32 {
        self.stuff + 1
    }
}

struct Alpha {
    base: Base,
    modifier: i32,
}

impl Foo for Alpha {
    fn foo(&self, a: i32, b: i32) -> i32 {
        (self.base.reusable() + a + b) * self.modifier
    }
}

您也可以将这两个概念结合起来,通过使用一个由类型参数约束的泛型


我非常支持Dietrich Epp的观点。使用一种新语言应该涉及到尝试新的编程范式。即使是在支持继承的语言中,出于代码重用的目的使用继承通常也不是一个好主意。相反地,创建小的构建块并将它们组合在一起。


8
你的回答中最重要的部分是这句话:“在我看来,使用一种新语言应该涉及到检查新范例。”这个想法经常被忽略了。 - Boiethios
继承用于代码重用的目的 - 为什么不是一个好主意?这不是面向对象编程的主要特点之一吗? - Yuri Feldman
2
@YuriFeldman 这个问题是关于C++的_virtual_函数,它们没有可重用的代码。 - Shepmaster
只有当您将“代码”定义为函数的主体时,@Shepmaster。我认为“接口”(以C ++意义而不是Java意义)也算作“代码”,因为将它们添加到类型中仍然对程序员构成了很多繁琐的工作(当您考虑到业务软件中一个类型可能有数十个成员时)。至少Rust具有traits / mixins,但这并不是完美的解决方案。 - Dai
2
不使用继承来实现代码复用并不是 Rust 特有的,也同样适用于 C++ (并且很多指导方针都鼓励这么做)。在 C++ 中,你可以使用纯接口,也可以通过模板实现编译时多态性。当然,这些都有一些棘手的问题,但它们确实存在,并没有什么“新范式”之类的东西涉及进来。 - rubenvb

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接