如何在Rust中声明接口?

问题描述 投票:6回答:1

我有多种使用相似方法的类型。我想通过编写一个接口来抽象它们,就像在Java中一样:

public interface Shape {
    public float area();
}

class Circle implements Shape {
    public float area() {
        return radius * radius * Math.PI;
    }

    public float radius;
}

但是,Rust中没有interface关键字。 Rust不提供抽象多种类型的可能性吗?

interface rust
1个回答
21
投票

TL; DR:Rust中最接近界面的是特征。但是,<< not >>是否希望它在所有方面都与接口相似。我的回答并非旨在详尽无遗,而是与来自其他语言的内容进行了比较。


如果您想要类似于接口的抽象,则需要使用Rust的trait

trait Shape { fn area(&self) -> f32; } struct Circle { radius: f32, } impl Shape for Circle { fn area(&self) -> f32 { self.radius.powi(2) * std::f32::consts::PI } } struct Square { side: f32, } impl Shape for Square { fn area(&self) -> f32 { self.side.powi(2) } } fn main() { display_area(&Circle { radius: 1. }); display_area(&Square { side: 1. }); } fn display_area(shape: &dyn Shape) { println!("area is {}", shape.area()) }

但是,

将Rust特征视为等同于OOP接口是错误的。

我将列举Rust的trait的一些特殊性。调度

在Rust中,可以使用in two ways进行分派(使用正确的数据和方法进行分派)。

静态发送当静态分配特征时,运行时没有开销。这等效于C ++模板;但是在C ++使用SFINAE的情况下,Rust编译器使用我们提供给他的“提示”检查有效性:

fn display_area(shape: &impl Shape) { println!("area is {}", shape.area()) }

[使用impl Shape,我们对编译器说我们的函数有一个实现Shape的泛型类型参数,因此我们可以在Shape::area上使用方法shape

在这种情况下,就像在C ++模板中一样,编译器将为传入的每个不同类型生成一个不同的函数。

动态调度

在我们的第一个示例中:

fn display_area(shape: &dyn Shape) { println!("area is {}", shape.area()) }

调度是动态的。这等效于在C#/ Java中使用接口或在C ++中使用抽象类。

在这种情况下,编译器并不关心shape的类型。正确的做法将在运行时确定,通常花费很少。

数据与实现之间的分离

如您所见,数据与实现分开;例如C#扩展方法。此外,特质的一种实用工具是在值上扩展可用的方法:

trait Hello { fn say_hello(&self); } impl Hello for &'static str { fn say_hello(&self) { println!("Hello, {}!", *self) } } fn main() { "world".say_hello(); }

的一个很大的优点是,您可以为数据实现特征,而无需修改数据。相反,在经典的面向对象的语言中,必须修改类以实现另一个接口。换句话说,您可以实现自己的外部数据特征。

This separation is true also at the lowest level。如果是动态调度,则为该方法提供两个指针:一个用于数据,另一个用于方法(vtable)。

默认实现

trait比经典接口有更多的东西:它可以提供方法的默认实现(就像Java 8中的“ ​​defender”方法一样)。示例:

trait Hello { fn say_hello(&self) { println!("Hello there!") } } impl Hello for i32 {} fn main() { 123.say_hello(); // call default implementation }

要使用经典的OOP单词,这就像没有变量成员的抽象类。

无继承

Rust trait的系统不是继承系统。例如,您不能尝试向下转换,也不能尝试将一个特性的引用投射到另一个特性。要获取有关此的更多信息,请参见this question about upcasting

此外,您可以使用dynamic type模拟所需的某些行为。

尽管您可以使用各种技巧在Rust中模拟继承机制,但是使用惯用设计而不是将语言扭曲为一种外来的思考方式(这将毫无用处地增加代码的复杂性)是一个更好的主意。

] >

您应该阅读Rust书籍中的the chapter about traits,以了解有关此主题的更多信息。

© www.soinside.com 2019 - 2024. All rights reserved.