I see the following pattern coming up a lot in Rust codebases, but I can't find an explanation why this is used.
For what end does one use the impl ... for construction?
Pseudocode:
impl Handler {
pub fn method1() -> () {
}
}
impl Dummy for Handler {
pub fn method2() -> () {
}
}
impl Struct ... adds some methods to Struct. These methods aren't available to other types or traits.
impl Trait for Struct .. implements the trait Trait for the struct Struct. This results in the methods of the trait being available for Struct.
So, even though these two syntaxes look similar, they do 2 completely different things.
impl Struct ... adds new (not previously defined) methods to the type, while the other adds previously defined methods (from the trait) to the type.
Related
There are 2 ways to provide methods for a Trait itself, Rustdoc distinguishes them by saying "provided methods" and impl dyn XXX. For example:
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
impl Trait {
fn bar(&self) {
println!("Anonymous implementation?");
}
}
I noticed it when I was reading the documentation of Rust's failure crate.
What are the use cases for them? What are the differences?
The first snippet,
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
implements a provided method on the trait. This method can be overridden by a trait implementation, but it does not have to be overridden.
The second snippet,
impl Trait {
fn bar(&self) {
println!("Anonymous implementation?");
}
}
implements an inherent method on a trait object of type dyn Trait. Method implementations for dyn Trait can only be called for trait objects, e.g. of type &dyn Trait. They can't receive self by value, since dyn Trait does not have a size known at compile time, and they can't be called on concrete types implementing Trait (including generic types with a Trait bound).
The modern notation is to write impl dyn Trait instead of impl Trait, and in fact this notation was one of the motivating examples for the introduction of the dyn keyword – the old syntax did not provide any clues as to what the semantics are, whereas the new syntax with the dyn keyword hints at the fact that this impl is only used together with dynamic dispatch.
A trait object is a fat pointer to an object implementing Trait, but the concrete type of the object is not necessarily known at compile time. The fat pointer contains a pointer to the object data, as well as a pointer to the virtual method table of the object type. The latter is used to dynamically dispatch to the correct trait implementation at runtime.
It is rather uncommon to use impl dyn Trait. Generally it's only useful if you want to make use of some dynamic type information, like downcasting to the actual type. The only traits with inherent methods on trait objects in the standard library are Any and Error.
In short: one can be overridden, and the other cannot.
When you define a trait, you define items that implementations of the trait may (or have to) override:
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
impl Trait for i64 {
fn foo(&self) {
println!("i64 implementation: {}", self);
}
}
On the other hand, using impl Trait, you define inherent methods, which cannot be overridden:
impl Trait {
fn bar(&self) {
self.foo();
self.foo()
}
}
// Try:
impl Trait for i64 {
fn bar(&self) { ... } // error: bar cannot be overridden.
}
As a result, inherent traits methods act as the Template Method Pattern: they provide a canvas linking together one or multiple overridable method(s).
If you look at the failure crate that you linked, the method Failure::find_root_cause() states:
This is equivalent to iterating over iter_causes() and taking the last item.
You may consider those inherent methods to be convenience methods, methods providing an easy/intuitive interface for common tasks which can be accomplished manually... but are conveniently pre-defined.
Note: any inherent method could be implemented as a free function taking the trait as a first argument; however free functions cannot be called in method position.
Consider the following Rust code
trait Trait {
fn show_name(&self) {}
}
struct Foo {
name: String,
things_and_stuff: usize,
}
impl Trait for Foo {
fn show_name(&self) {
println!("{}", self.name);
}
}
struct Bar {
name: String,
other_field: i32,
}
impl Trait for Bar {
fn show_name(&self) {
println!("{}", self.name);
}
}
The two show_name functions have exactly the same code. It would be convenient if I could put that method as a default method on Trait, but that's not possible because traits cannot access struct fields.
We could declare a get_name(&self) -> &str method on Trait and implement it on Foo and Bar, but that doesn't fix the problem of having duplicated code because both implementations of get_name will be the same.
It would be nice to avoid the code duplication. Another question has already asked if field access is possible in traits, and the answer was basically "no".
However, I found a comment in the rust-internals forum suggesting that it's already possible. Here's the code:
struct Fields {
name: String,
}
trait Trait: BorrowMut<Fields> {
// methods go here
}
impl<T> Trait for T where T: BorrowMut<Fields> {}
Presumably there's a way to make a type T be BorrowMut<Fields> and use that to allow Trait to access Fields's fields, but so far I'm not sure how that would work.
How is the code snippet shown above supposed to solve the problem of getting field access in traits?
I know there are discussions of adding field access in traits to the language (rust-internals, RFC, another RFC), but I'd like to know what's possible now.
the answer was basically "no"
The answer actually says (emphasis mine):
A default implementation can only use methods that are defined on the trait or in a super trait.
That's what your snippet does:
trait Trait: BorrowMut<Fields>
To make it work, follow the advice from the post you are referencing:
all types which choose to implement BorrowMut<Foo>
Thus, you need to implement the trait for each of your types. I switched to Borrow because you don't need mutability here:
use std::borrow::Borrow;
struct Fields {
name: String,
}
trait Trait: Borrow<Fields> {
fn show_name(&self) {
let fields: &Fields = self.borrow();
println!("{}", fields.name);
}
}
struct Foo {
fields: Fields,
things_and_stuff: usize,
}
impl Borrow<Fields> for Foo {
fn borrow(&self) -> &Fields {
&self.fields
}
}
struct Bar {
fields: Fields,
other_field: i32,
}
impl Borrow<Fields> for Bar {
fn borrow(&self) -> &Fields {
&self.fields
}
}
impl<T> Trait for T where T: Borrow<Fields> {}
I'm almost certain you won't like this solution because it
doesn't fix the problem of having duplicated code because both implementations [...] will be the same
You may prefer to write a macro if your goal is to reduce the number of duplicate characters in your code.
See also:
I implemented a trait for another trait but cannot call methods from both traits
Is it possible to access struct fields from within a trait?
Defining the methods of generic type requires adding generic types after impl:
struct GenericVal<T>(T,);
impl <T> GenericVal<T> {}
I feel that removing <T> seems OK:
struct GenericVal<T>(T,);
impl GenericVal<T> {}
Is it any special consideration?
Rust allows you to write impl blocks that apply only to some specific combination of type parameters. For example:
struct GenericVal<T>(T);
impl GenericVal<u32> {
fn foo(&self) {
// method foo() is only defined when T = u32
}
}
Here, the type GenericVal is generic, but the impl itself is not.
Thus, if you want to write an impl block that applies for all GenericVal<T> types, you must first declare a type parameter on the impl itself (otherwise, T would try to look up a type named T).
struct GenericVal<T>(T);
impl<T> GenericVal<T> {
fn foo(&self) {
// method foo() is always present
}
}
This declaration also lets you have a single type parameter that can be used multiple times, forcing the types to be the same.
struct GenericVal<T, U>(T, U);
impl<V> GenericVal<V, V> {
fn foo(&self) {
// method foo() is only defined when T = U
}
}
I have a collection of Trait, a function that iterates over it and does something, and then I would like to check the implementor type and if it is of type Foo then downcast it and call some Foo method.
Basically, something similar to Go's type-switch and interface conversion.
Searching around I found about the Any trait but it can only be implemented on 'static types.
To help demonstrate what I want:
let vec: Vec<Box<Trait>> = //
for e in vec.iter() {
e.trait_method();
// if typeof e == Foo {
// let f = e as Foo;
// f.foo_method();
//}
}
As you have noticed, downcasting only works with Any trait, and yes, it only supports 'static data. You can find a recent discussion on why it is so here. Basically, implementing reflection for references of arbitrary lifetimes is difficult.
It is also impossible (as of now, at least) to combine Any with your custom trait easily. However, a macro library for automatic implementation of Any for your trait has recently been created. You can also find some discussion on it here.
This isn't a Rust-specific problem, although the vocabulary may be a little different. The ideal way to solve a problem like this, not just with traits in Rust but in any language, is to add the desired behavior (foo_method in your example) to the abstract interface (Trait):
trait Trait {
fn trait_method(&self);
fn foo_method(&self) {} // does nothing by default
}
struct Foo;
impl Trait for Foo {
fn trait_method(&self) {
println!("In trait_method of Foo");
}
fn foo_method(&self) {
// override default behavior
println!("In foo_method");
}
}
struct Bar;
impl Trait for Bar {
fn trait_method(&self) {
println!("In trait_method of Bar");
}
}
fn main() {
let vec: Vec<Box<dyn Trait>> = vec![Box::new(Foo), Box::new(Bar)];
for e in &vec {
e.trait_method();
e.foo_method();
}
}
In this example, I have put a default implementation of foo_method in Trait which does nothing, so that you don't have to define it in every impl but only the one(s) where it applies. You should really attempt to make the above work before you resort to downcasting to a concrete type, which has serious drawbacks that all but erase the advantages of having trait objects in the first place.
That said, there are cases where downcasting may be necessary, and Rust does support it -- although the interface is a little clunky. You can downcast &Trait to &Foo by adding an intermediate upcast to &Any:
use std::any::Any;
trait Trait {
fn as_any(&self) -> &dyn Any;
}
struct Foo;
impl Trait for Foo {
fn as_any(&self) -> &dyn Any {
self
}
}
fn downcast<T: Trait + 'static>(this: &dyn Trait) -> Option<&T> {
this.as_any().downcast_ref()
}
as_any has to be a method in Trait because it needs access to the concrete type. Now you can attempt to call Foo methods on a Trait trait object like this (complete playground example):
if let Some(r) = downcast::<Foo>(&**e) {
r.foo_method();
}
To make this work, you have to specify what type you expect (::<Foo>) and use if let to handle what happens when the referenced object is not an instance of Foo. You can't downcast a trait object to a concrete type unless you know exactly what concrete type it is.
If you ever need to know the concrete type, trait objects are almost useless anyway! You probably should use an enum instead, so that you will get compile-time errors if you omit to handle a variant somewhere. Furthermore, you can't use Any with non-'static structs, so if any Foo might need to contain a reference, this design is a dead end. The best solution, if you can do it, is to add foo_method to the trait itself.
I have a collection of Trait, a function that iterates over it and does something, and then I would like to check the implementor type and if it is of type Foo then downcast it and call some Foo method.
Basically, something similar to Go's type-switch and interface conversion.
Searching around I found about the Any trait but it can only be implemented on 'static types.
To help demonstrate what I want:
let vec: Vec<Box<Trait>> = //
for e in vec.iter() {
e.trait_method();
// if typeof e == Foo {
// let f = e as Foo;
// f.foo_method();
//}
}
As you have noticed, downcasting only works with Any trait, and yes, it only supports 'static data. You can find a recent discussion on why it is so here. Basically, implementing reflection for references of arbitrary lifetimes is difficult.
It is also impossible (as of now, at least) to combine Any with your custom trait easily. However, a macro library for automatic implementation of Any for your trait has recently been created. You can also find some discussion on it here.
This isn't a Rust-specific problem, although the vocabulary may be a little different. The ideal way to solve a problem like this, not just with traits in Rust but in any language, is to add the desired behavior (foo_method in your example) to the abstract interface (Trait):
trait Trait {
fn trait_method(&self);
fn foo_method(&self) {} // does nothing by default
}
struct Foo;
impl Trait for Foo {
fn trait_method(&self) {
println!("In trait_method of Foo");
}
fn foo_method(&self) {
// override default behavior
println!("In foo_method");
}
}
struct Bar;
impl Trait for Bar {
fn trait_method(&self) {
println!("In trait_method of Bar");
}
}
fn main() {
let vec: Vec<Box<dyn Trait>> = vec![Box::new(Foo), Box::new(Bar)];
for e in &vec {
e.trait_method();
e.foo_method();
}
}
In this example, I have put a default implementation of foo_method in Trait which does nothing, so that you don't have to define it in every impl but only the one(s) where it applies. You should really attempt to make the above work before you resort to downcasting to a concrete type, which has serious drawbacks that all but erase the advantages of having trait objects in the first place.
That said, there are cases where downcasting may be necessary, and Rust does support it -- although the interface is a little clunky. You can downcast &Trait to &Foo by adding an intermediate upcast to &Any:
use std::any::Any;
trait Trait {
fn as_any(&self) -> &dyn Any;
}
struct Foo;
impl Trait for Foo {
fn as_any(&self) -> &dyn Any {
self
}
}
fn downcast<T: Trait + 'static>(this: &dyn Trait) -> Option<&T> {
this.as_any().downcast_ref()
}
as_any has to be a method in Trait because it needs access to the concrete type. Now you can attempt to call Foo methods on a Trait trait object like this (complete playground example):
if let Some(r) = downcast::<Foo>(&**e) {
r.foo_method();
}
To make this work, you have to specify what type you expect (::<Foo>) and use if let to handle what happens when the referenced object is not an instance of Foo. You can't downcast a trait object to a concrete type unless you know exactly what concrete type it is.
If you ever need to know the concrete type, trait objects are almost useless anyway! You probably should use an enum instead, so that you will get compile-time errors if you omit to handle a variant somewhere. Furthermore, you can't use Any with non-'static structs, so if any Foo might need to contain a reference, this design is a dead end. The best solution, if you can do it, is to add foo_method to the trait itself.