I have read the book and docs but still couldn't get what is the exact purpose of traits. I mean i can do all stuffs traits do by using impl fn to attach method to structs.
You can't attach impl fn to a type which isn't owned by you, i.e. which is defined in your dependencies (including stdlib).
There is also quite a lot of compile time programming going through traits, e.g. you can take a look at the typenum and generic-array crates, which allow you to work with compile-time computations on generic constants (which are poorly supported otherwise in current Rust) and arrays. Even stuff like the Send/Sync system, which is at the core of Rust's fearless concurrency and multithreading, is an instance of such compile-time programming magic.
But the primary reason to use traits is generic programming. You can't do anything with a generic parameter, unless you specify that it implements some trait, in which case you can use the methods from that trait. This also applies to dynamically dispatched generic programming: you can only deal with explicit trait object. Pretty much anything requiring dynamic dispatch, and most generic interfaces, explicitly require traits. Loads of traits.
Just to add to what @afetisov wrote, my understanding is that Rust's "traits" are like Haskell typeclasses. That facility has existed for a long, long time, so you might find good tutorial introductions to typeclasses to be useful for understanding traits.
Short answer:
impl
on a type adds functionality to one particular type. The methods are available to that one type only. Even if there are other types with methods having the same name, these implementations will be distinct.
Traits can define methods that can be implemented by many different types. By writing impl Trt for Tpe
, you say that a type Tpe
will have all methods of Trt
. Thus several different types share the same methods (the methods defined by the trait). Consequently, you can write generic functions which accept arbitrary types as arguments as long as they implement certain traits (see trait bounds).
Depending on what languages you're coming from, this may be surprising, but: If the compiler can't prove a method exists that takes all the expected parameters and so on, your code won't compile in Rust. So while in some langauges you could do something like
fn foo<Obj>(some_object_of_any_type: Obj) {
let obj = some_object_of_any_type; // ok that name is too long
if obj.some_method(42) {
// ...
}
}
And at run time, the language will try to find some_method
for Obj
and call it somehow, and coerce the result into a boolean, etc. But you can't do that in Rust: it's all statically (compile-time) determined, so you need some "proof" that this generic type has such a method, and in fact, exactly which method it is.
Hence traits and trait bounds:
trait SomeTrait {
fn some_method(&self, i: i32) -> bool;
}
fn foo<Obj: SomeTrait>(obj: Obj) {
// new ^^^^^^^^^^^
if obj.some_method(42) {
// ...
}
}
You've now declared "foo
needs types that implement SomeTrait
, but any type that implements SomeTrait
will do." And the compiler can find the some_method
method in SomeTrait
, so it's satisfied.
To actually make call this, you would have to implement the trait:
// Note that you can do this even though you don't "own" `&str`
// (because you do "own" the local trait)
impl SomeTrait for &str {
fn some_method(&self, i: i32) -> bool {
i == 0
}
}
fn main() {
foo("");
}
Adding the method to your own types directly (impl MyType { ... }
) won't work and won't be used; you have to implement the trait (impl SomeTrait for MyType { ... }
).
You can wrap the type you want to implement with a local struct, then you can impl fn for the local type.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.