Note that Traits are a system for generics specifically. You can have useful OO without generics (C++ is useful without templates, go does't have them at all).
Note that Traits are a system for generics specifically. You can have useful OO without generics (C++ is useful without templates, go does't have them at all).
With Traits you can separate implementation of certain methods (implementation of Traits) from object definitions. Which for me is excellent, and approaches CLOS' way (in CLOS methods are not directly coupled to a particular object; methods are applied to the combination of one (or more) object(s).
In a similar way as English language; where you apply an action involving many subects: "Angel transport his luggage using a car." Here the 'transport' action can't really be bound to the Person class, nor the Luggage class. Perhaps it belongs on the Car class, but with CLOS you can just define "transport" to act on the combination of <actor> <object to be transported> and <vehicle>.
"Transport", thus, is a "generic method" and can be defined (implemented) for all kinds of class combinations.
With Traits you can achieve something more or less similar, by defining the necessary Traits for each kind of class, to be able to have all of them "transport" harmoniously. So Traits can link (and then impement) the operations performed between classes, without having to bound the code to a particular class.
However, note that there does exist a very important generic function in the stdlib that doesn't use traits at all: `std::mem::drop`. Here's the source, which is very, very interesting: https://github.com/rust-lang/rust/blob/4ed2edaafe82fb8d44e81... . And no, I'm not being facetious when I call it interesting; figuring out what `std::mem::drop`'s purpose is from its amusing implementation is a test that you understand what ownership means in Rust. :)
Traits do a lot of things in Rust that have nothing to do with generics. :) Implementing a trait is how one gives a type a destructor. Implementing various traits (the ones in std::ops) allows one to do operator overloading for user-defined types. Defining a trait allows one to give methods to types that are defined in other compilation units, including the standard library, e.g.:
trait Foo {
fn foo(self);
}
impl Foo for i32 {
fn foo(self) {
println!("Hello, traits!");
}
}
fn main() {
42.foo(); // Hello, traits!
}https://doc.rust-lang.org/std/io/trait.Read.html https://doc.rust-lang.org/std/io/trait.Write.html
fn identity<T>(t: T) -> T { t }
Has no trait bounds, and is clearly generic. You typically can't do very much with completely free type variables, but Rust has syntactic support for them.