How do you declare an interface in 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.


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())
}

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.

Dispatch

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

Static dispatch

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(shape: &impl Shape) {
    println!("area is {}", shape.area())
}

With impl Shape, we say to the compiler that our function has a generic type parameter that implements Shape, therefore we can use the method Shape::area on our shape.

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

Dynamic dispatch

In our first example:

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

the dispatch is dynamic. This is an equivalent to using an interface in C#/Java or an abstract class in 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, usually at a very slight cost.

Separation between data and implementation

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();
}

A great advantage of this, is that you can implement a trait for a data without modifying the data. In contrast, in classical object oriented languages, you must modify the class to implement another interface. Said otherwise, you can implement your own traits for external data.

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).

Default implementation

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
}

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

No inheritance

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.

While you can simulate the inheritance mechanism in Rust with various tricks, this is a better idea to use idiomatic designs instead of twist the language to a foreign way of thinking that will uselessly make grow the complexity of code.

You should read the chapter about traits in the Rust book to learn more about this topic.

Leave a Comment

techhipbettruvabetnorabahisbahis forumuedueduedusedueduseduedueduseduedus