你如何在Rust中声明一个接口? [英] How do you declare an interface in Rust?

查看:1692
本文介绍了你如何在Rust中声明一个接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have multiple types with similar methods. I want to abstract over them by writing an interface, like I would in Java:

public interface Shape {
    public float area();
}

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

    public float radius;
}

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

However, there is no interface keyword in Rust. Doesn't Rust offer the possibility to abstract over multiple types?

推荐答案

TL; DR :最接近Rust的接口是一个特征。但是,期望它在所有方面与接口相似。我的答案并非旨在详尽无遗,而是提供一些与其他语言相比较的元素。

TL;DR: The closest to interface in Rust is a trait. However, do not expect it to be similar in all point to an interface. My answer does not aim to be exhaustive but gives some elements of comparison to those coming from other languages.

如果你想要类似于接口的抽象,你需要使用Rust的 trait s:

If you want an abstraction similar to interface, you need to use Rust's traits:

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的特征 s的一些特殊情况。

However, it is an error to see a Rust trait as an equivalent of OOP interface. I will enumerate some particularities of Rust's traits.

在Rust中,可以完成派遣(使用正确的数据和方法时给定特征)以两种方式

In Rust, the dispatch (i.e. using the right data and methods when given a trait) can be done in two ways:

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

When a trait is statically dispatched, there is no overhead at runtime. This is an equivalent of C++ templates; but where C++ uses SFINAE, the Rust compiler checks the validity using the "hints" we give to him:

fn display_area<T>(shape: &T)
where
    T: Shape,
{
    println!("area is {}", shape.area())
}

全部是明确的:带有其中T:Shape ,我们告诉编译器我们的泛型类型 T 实现 Shape ,因此我们可以使用方法<$我们的形状上的c $ c> Shape :: area

All is explicit: with where T: Shape, we say to the compiler that our generic type T implements Shape, therefore we can use the method Shape::area on our shape.

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

In this case, like in C++ templates, the compiler will generate a different function for each different type passed in.

在我们的第一个例子中:

In our first example:

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

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

the dispatch is dynamic. This is an equivalent to using an interface in C#/Java or an abstract class in C++.

在这种情况下,编译器不关心<$ c的类型$ C>形状。正确的做法将在运行时确定,成本非常低。

In this case, the compiler does not care about the type of shape. The right thing to do with it will be determined at runtime, at a very slight cost.

如您所见,数据与实现分开;例如,C#扩展方法。此外,特征的一个实用程序是扩展值的可用方法:

As you see, the data is separated from the implementation; like, for example, C# extension methods. Moreover, one of the utilities of a trait is to extend the available methods on a value:

trait Hello {
    fn say_hello(&self);
}

impl Hello for &'static str {
    fn say_hello(&self) {
        println!("Hello, {}!", *self)
    }
}

fn main() {
    "world".say_hello();
}

这种分离在最低级别也是如此。在动态调度的情况下,该方法有两个指针:一个用于数据,另一个用于方法(vtable)。

This separation is true also at the lowest level. In case of dynamic dispatch, the method is given two pointers: one for the data, and another for the methods (the vtable).

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

The trait has one more thing than a classic interface: it can provide a default implementation of a method (just like the "defender" method in Java 8). Example:

trait Hello {
    fn say_hello(&self) {
        println!("Hello there!")
    }
}

impl Hello for i32 {}

fn main() {
    123.say_hello(); // call default implementation
}

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

To use classic OOP words, this is like an abstract class without variable members.

Rust特征的系统不是继承系统。例如,您不能尝试向下转换或尝试将特征的引用转换为其他特征。要获得有关此内容的更多信息,请参阅有关上传的问题

The Rust trait's system is not an inheritance system. You cannot try to downcast, for example, or try to cast a reference on a trait to another trait. To get more information about this, see this question about upcasting.

此外,您可以使用动态类型来模拟你想要的一些行为。

Moreover, you can use the dynamic type to simulate some behavior you want.

虽然你可以用各种技巧模拟Rust中的继承机制,但这是更好的想法是使用惯用设计,而不是将语言转换为外国的思维方式,这将无济于事地增加代码的复杂性。

这篇关于你如何在Rust中声明一个接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆