Rust's trait features
"TLDR: This article provides a detailed introduction to the trait feature in the Rust language, covering its basic usage, inheritance and multiple inheritance operations, as well as static dispatch and dynamic dispatch mechanisms. Through example code, it demonstrates how to define and implement traits, and how to use the Send and Sync constraints to restrict implementors' capabilities. The article also compares the differences between traits and interfaces, and explains the principles and performance differences between static dispatch (determining types at compile time and generating efficient code) and dynamic dispatch (looking up concrete implementations at runtime)."
Rust also has a feature similar to interfaces in other languages, namely traits. However, Rust has enhanced them to enable many more functionalities.
Basic Usage
pub trait Parser{
fn parse(&self, input: &str) -> String;
}
Now let's implement this trait:
struct CommonVideoParser;
impl Parser for CommonVideoParser{
fn parse(&self, input: &str) -> String {
fomat!("Parsed: {}", input)
}
}
Additionally, you can perform operations similar to inheritance and multiple inheritance.
pub trait Parser: Send {
fn parse(&self, input: &str) -> String;
}
pub trait Parser: Send + Sync {
fn parse(&self, input : & str) -> String;
}
Sendis a trait bound provided by Rust, used to constrain implementors. It mainly means that the value of this type can be transferred to another thread.Syncmeans that the reference&Tof this type can be safely shared between threads.
Differences from Interfaces
A trait is roughly equivalent to an interface, but it is more powerful.
Static Dispatch
The compiler knows the type at compile time and directly generates efficient code. This generic + trait approach is similar to C++ templates:
fn run<T: Parser>(p: T) {
println!("{}", p.parse("hi"));
}
When we pass in CommonVideoParser, the compiler knows that T is of type CommonVideoParser (previously, it only knew that p was something implementing the Parser trait, but not exactly which one), and then directly generates a specialized version of the run code written specifically for CommonVideoParser.
Dynamic Dispatch
Similar to Java's polymorphism, implemented using a vtable.
fn run(p: &dyn Parser) {
println!("{}", p.parse("hi"));
}
In this run function, when p is passed in as CommonVideoParser, p is a &dyn Parser, so the compiler only knows that p is something that implements the Parser trait, but not exactly what it is. It needs to look up the specific implementation at runtime as needed. This is similar to Java's polymorphism, but with slightly lower performance.