Traits, very similar to Java's interface feature, are common sets of methods that can be implemented into different types. Most (if not all) of the structs and enums in the standard Rust library implement traits that are used to alter their behaviour, or implement similar methods across multiple different types.
For instance, the Copy trait seen earlier implements methods on types that are small enough to be
safely copied, rather than borrowed.
The Clone trait, conversely, implements a method on types that can't be safely
copied, called clone(), which copies the entire value from one space to another in heap memory, which may negate
the need to borrow the value in some cases, but can slow down execution.
There's also a Sized trait, which marks types whose sizes in memory can be ascertained at compile-time, before the program begins execution. A variant of this trait, ?Sized, can be used to give the option not to enforce the behaviour of the trait, if appropriate.
When defining a struct or enum, pre-existing traits defined in the standard Rust library can be explicitly implemented with the derive attribute. This is written on the line before the definition of the data type in question, and multiple traits can be implemented at once:
#[derive(Clone, Debug)]
struct Cloneable {
string: String,
integer: i32,
float: f64,
character: char,
boolean: bool
}
fn main() {
let cln = Cloneable {
string: String::from("hello"),
integer: 28,
float: 169.3487,
character: 'A',
boolean: true
};
println!("{:?}", dbg!(&cln)); // displayable with debugging macro
println!("{:?}", cln.clone()) // cloneable
}
Of course, the programmer can write and implement their own structs, using the trait and impl keywords:
struct Cloneable {
string: &'static str,
integer: i32,
float: f64,
character: char,
boolean: bool
}
trait ChangeString {
fn change_string(&mut self, val: &'static str) -> &'static str ;
}
impl ChangeString for Cloneable {
fn change_string(&mut self, val: &'static str) -> &'static str {
self.string = val;
self.string
}
}
fn main() {
let mut cln = Cloneable {
string: "hello",
integer: 28,
float: 169.3487,
character: 'A',
boolean: true
};
println!("{}", cln.change_string("hi!"))
}
Methods in traits are usually defined without bodies, only type definitions, and they have code written for them per implementation.