Traits
A trait is a collection of methods.
Data types can implement traits. To do so, the methods making up the trait are defined for the data type. For example, the String data type implements the From<&str> trait. This allows a user to write String::from("hello").
In this way, traits are somewhat similar to Java interfaces and C++ abstract classes.
Some additional common Rust traits include:
Clone(theclonemethod)Display(which allows formatted display via{})Debug(which allows formatted display via{:?})
Because traits indicate shared behavior between data types, they are useful when writing generics.
Further information
Rustlings
traits1: Time to implement some traits!
// traits1.rs // Time to implement some traits! // // Your task is to implement the trait // `AppendBar` for the type `String`. // // The trait AppendBar has only one function, // which appends "Bar" to any object // implementing this trait. // Execute `rustlings hint traits1` or use the `hint` watch subcommand for a hint. // I AM NOT DONE trait AppendBar { fn append_bar(self) -> Self; } impl AppendBar for String { // TODO: Implement `AppendBar` for type `String`. } fn main() { let s = String::from("Foo"); let s = s.append_bar(); println!("s: {}", s); // convert unit test to play in playground assert_eq!( String::from("Foo").append_bar(), String::from("FooBar") ); assert_eq!( String::from("").append_bar().append_bar(), String::from("BarBar") ); } // #[cfg(test)] // mod tests { // use super::*; // // #[test] // fn is_foo_bar() { // assert_eq!(String::from("Foo").append_bar(), String::from("FooBar")); // } // // #[test] // fn is_bar_bar() { // assert_eq!( // String::from("").append_bar().append_bar(), // String::from("BarBar") // ); // } // }
Hint
A discussion about Traits in Rust can be found at: 🪐Traits: Defining Shared Behavior - The Rust Programming Language
solution: String
impl AppendBar for String { //Add your code here fn append_bar(self) -> Self { format!("{}Bar", self) // or // format!("{self}Bar") // or // self + "Bar" } }
traits2: implement the trait for a vector of strings.
// traits2.rs // // Your task is to implement the trait // `AppendBar` for a vector of strings. // // To implement this trait, consider for // a moment what it means to 'append "Bar"' // to a vector of strings. // // No boiler plate code this time, // you can do this! // Execute `rustlings hint traits2` or use the `hint` watch subcommand for a hint. // I AM NOT DONE trait AppendBar { fn append_bar(self) -> Self; } // TODO: Implement trait `AppendBar` for a vector of strings. // #[cfg(test)] // mod tests { // use super::*; // // #[test] // fn is_vec_pop_eq_bar() { // let mut foo = vec![String::from("Foo")].append_bar(); // assert_eq!(foo.pop().unwrap(), String::from("Bar")); // assert_eq!(foo.pop().unwrap(), String::from("Foo")); // } // } fn main() { // convert unit test to main to play in playground let mut foo = vec![String::from("Foo")].append_bar(); assert_eq!(foo.pop().unwrap(), String::from("Bar")); assert_eq!(foo.pop().unwrap(), String::from("Foo")); }
Hint
Notice how the trait takes ownership of ‘self’,and returns Self.
Try mutating the incoming string vector. Have a look at the tests to see
what the result should look like!
Vectors provide suitable methods for adding an element at the end. See the documentation at: Vec in std::vec - Rust
solution: Vec
// Add your code here impl AppendBar for Vec<String> { fn append_bar(mut self) -> Self { // Borrow self as `mut` self.push("Bar".to_string()); self } }
traits3: implement the Licensed trait for both structures and have them return the same information without writing the same function twice.
// traits3.rs // // Your task is to implement the Licensed trait for // both structures and have them return the same // information without writing the same function twice. // // Consider what you can add to the Licensed trait. // Execute `rustlings hint traits3` or use the `hint` watch subcommand for a hint. // I AM NOT DONE pub trait Licensed { fn licensing_info(&self) -> String; } struct SomeSoftware { version_number: i32, } struct OtherSoftware { version_number: String, } impl Licensed for SomeSoftware {} // Don't edit this line impl Licensed for OtherSoftware {} // Don't edit this line // #[cfg(test)] // mod tests { // use super::*; // // #[test] // fn is_licensing_info_the_same() { // let licensing_info = String::from("Some information"); // let some_software = SomeSoftware { version_number: 1 }; // let other_software = OtherSoftware { // version_number: "v2.0.0".to_string(), // }; // assert_eq!(some_software.licensing_info(), licensing_info); // assert_eq!(other_software.licensing_info(), licensing_info); // } // } fn main() { // convert unit test to main to play in playground let licensing_info = String::from("Some information"); let some_software = SomeSoftware { version_number: 1 }; let other_software = OtherSoftware { version_number: "v2.0.0".to_string(), }; assert_eq!(some_software.licensing_info(), licensing_info); }
Hint
Traits can have a default implementation for functions. Structs that implement the trait can then use the default version of these functions if they choose not implement the function themselves.
See the documentation at: 🪐Traits: Defining Shared Behavior >> Default Implementations - The Rust Programming Language
solution: Default Trait Method
pub trait Licensed { fn licensing_info(&self) -> String { String::from("Some information") } }
traits4: trait bounds
// traits4.rs // // Your task is to replace the '??' sections so the code compiles. // Don't change any line other than the marked one. // Execute `rustlings hint traits4` or use the `hint` watch subcommand for a hint. // I AM NOT DONE pub trait Licensed { fn licensing_info(&self) -> String { "some information".to_string() } } struct SomeSoftware {} struct OtherSoftware {} impl Licensed for SomeSoftware {} impl Licensed for OtherSoftware {} // YOU MAY ONLY CHANGE THE NEXT LINE fn compare_license_types(software: ? ?, software_two: ? ?) -> bool { software.licensing_info() == software_two.licensing_info() } // #[cfg(test)] // mod tests { // use super::*; // // #[test] // fn compare_license_information() { // let some_software = SomeSoftware {}; // let other_software = OtherSoftware {}; // // assert!(compare_license_types(some_software, other_software)); // } // // #[test] // fn compare_license_information_backwards() { // let some_software = SomeSoftware {}; // let other_software = OtherSoftware {}; // // assert!(compare_license_types(other_software, some_software)); // } // } fn main() { // convert unit test to main to play in playground // compare_license_information let some_software = SomeSoftware {}; let other_software = OtherSoftware {}; assert!(compare_license_types(some_software, other_software)); // compare_license_information_backwards let some_software = SomeSoftware {}; let other_software = OtherSoftware {}; assert!(compare_license_types(other_software, some_software)); }
Hint
Instead of using concrete types as parameters you can use traits. Try replacing the ‘??’ with ‘impl <what goes here?>’
See the documentation at: 🪐Traits: Defining Shared Behavior >> Traits as Parameters - The Rust Programming Language
solution: impl
fn compare_license_types(software: impl Licensed, software_two: impl Licensed) -> bool { software.licensing_info() == software_two.licensing_info() }
traits5: impl multiple traits
// traits5.rs // // Your task is to replace the '??' sections so the code compiles. // Don't change any line other than the marked one. // Execute `rustlings hint traits5` or use the `hint` watch subcommand for a hint. // I AM NOT DONE pub trait SomeTrait { fn some_function(&self) -> bool { true } } pub trait OtherTrait { fn other_function(&self) -> bool { true } } struct SomeStruct {} struct OtherStruct {} impl SomeTrait for SomeStruct {} impl OtherTrait for SomeStruct {} impl SomeTrait for OtherStruct {} impl OtherTrait for OtherStruct {} // YOU MAY ONLY CHANGE THE NEXT LINE fn some_func(item: ??) -> bool { item.some_function() && item.other_function() } fn main() { some_func(SomeStruct {}); some_func(OtherStruct {}); }
Hint
To ensure a parameter implements multiple traits use the ‘+ syntax’. Try replacing the ‘??’ with ‘impl <> + <>’.
See the documentation at: Specifying Multiple Trait Bounds with the + Syntax