Today I saw for the first time the impl dyn Trait
syntax in this PR. I had never seen this before and a quick search did not yield any results. After some experimentation I turned out that this syntax is quite useful.
The impl dyn Trait
syntax allows you to implement methods that work for any object that implements a certain trait (including trait objects) without that method being part of the trait definition. In particular this means that implementer cannot override these methods and break contracts.
Let me use the following (very contrived) example to explain how this is useful. Assume you have a trait that allows you to convert a value into a bool
. It would look like this.
trait AsBool {
fn as_bool(&self) -> bool
}
Now it turns out that in your code that you often check if value that implements AsBool
converts to false
, for example
fn ban_user(am_i_admin: impl AsBool, other: User) {
if (!am_i_admin.as_bool()) {
panic!("Your not an admin!")
}
// ...
}
To make your code more dry and still have it work with any AsBool
implementation you can add the following trait method with a default implementation
trait AsBool {
fn as_bool(&self) -> bool;
fn is_false(&self) -> bool {
!self.as_bool()
}
}
The problem with this approach is that implementors of AsBool
might choose to override this implementation and violate the contract that x.as_bool() == !x.is_false()
.
A solution to this would be to write a static function
fn is_false(x: impl AsBool) -> bool {
!x.as_bool()
}
However, using methods calls is often more convenient and idiomatic in Rust. This is where the impl dyn
syntax comes into play. We can write the following:
impl dyn AsBool {
fn is_false(&self) -> bool {
!self.as_bool()
}
}
This allows us to call x.is_false()
whenever x
implements AsBool
or x
is a reference or smart pointer to a value that implements AsBool
.