Rustlings Topic: Traits

A trait tells the Rust compiler about functionality a particular type has and can share with other types. We can use traits to define shared behavior in an abstract way. We can use trait bounds to specify that a generic type can be any type that has certain behavior.

Traits are similar to a feature often called interfaces in other languages, although with some differences.

trait is one of the most important Rust feature that you must be familiar with. There are so many trait that Rust provides. Such as Clone, Copy, Debug, Default, Deref, From, Ord etc.

Sometimes you can use Derive macros macro, or sometimes you have to implement trait by yourself. Unfortunately, rustling only focuses on the basic usage of the trait rather than introducing Rust providing traits. So if you want to know more about common traits, I suggest you to read blog article.

You may find solution code for the topic from my repo.

  1. traits1.rs
  2. traits2.rs

traits1.rs

trait is similar to the struct in grammar. It has two parts:

trait MyTrait {
    // Definition of the trait
    // Required method definition goes here.
    fn trait_func(self) -> Self;
}

impl MyTrait for MyStruct {
    // Implementation
}

In this example, we have to implement impl block for the trait AppendBar.

/* file: "exercises/traits/traits1.rs" */
trait AppendBar {
    fn append_bar(self) -> Self;
}

impl AppendBar for String {
    fn append_bar(self) -> Self {
        let mut cloned_self = self.clone();
        cloned_self.push_str("Bar");
        cloned_self
    }
}

fn main() {
    let s = String::from("Foo");
    let s = s.append_bar();
    println!("s: {}", s);
}

#[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")
        );
    }
}

traits2.rs

traits2.rs is almost identical to the previous problem. The only difference is that now we have to implement trait AppendBar for the Vec<String> instead of String.

The problems are very easy to solve. But trait has so many potential and so many common trait that Rust provides. So please take a look at blog article when you have time to find out common traits that you should know.

/* file: "exercises/traits/traits2.rs" */
trait AppendBar {
    fn append_bar(self) -> Self;
}

impl AppendBar for Vec<String> {
    fn append_bar(self) -> Self {
        let mut cloned_self = self.clone();
        cloned_self.push("Bar".to_string());
        cloned_self
    }
}

#[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"));
    }
}

Continue with Rustlings Solution