> For the complete documentation index, see [llms.txt](https://bagus-cahyono.gitbook.io/programming-notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://bagus-cahyono.gitbook.io/programming-notes/rust/rustlings_exercise/15_traits.md).

# Traits

* A trait is similar to an interface in other programming languages.
* It defines a set of methods that a type must implement, allowing you to specify what functionality a type provides without dictating how it provides it.
* Implementing a trait on a type is similar to implementing regular methods.
* The difference is that after `impl`, we put the trait name we want to implement, then use the `for` keyword, and then specify the name of the type we want to implement the trait for.
* Traits can provide default method implementations that types can override.
* The `impl Trait` syntax works for straightforward cases but is actually syntax sugar for a longer form known as a trait bound.
* We can also specify more than one trait bound using `+` syntax.
* References:
  * [Traits](https://doc.rust-lang.org/book/ch10-02-traits.html)
  * [Traits - Rust by Example](https://doc.rust-lang.org/rust-by-example/trait.html)

## traits1.rs

```rust
// The trait `AppendBar` has only one function which appends "Bar" to any object
// implementing this trait.
trait AppendBar {
    fn append_bar(self) -> Self;
}

impl AppendBar for String {
    // Implement `AppendBar` for the type `String`.
    fn append_bar(self) -> Self {
        self + "Bar"
    }
}

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(), "FooBar");
    }

    #[test]
    fn is_bar_bar() {
        assert_eq!(String::from("").append_bar().append_bar(), "BarBar");
    }
}
```

* This exercise is easy we just need to implement trait item/function `append_bar` inside the `impl AppendBar for String` block.

## traits2.rs

```rust
trait AppendBar {
    fn append_bar(self) -> Self;
}

// Implement the trait `AppendBar` for a vector of strings.
// `append_bar` should push the string "Bar" into the vector.
impl AppendBar for Vec<String> {
    fn append_bar(mut self) -> Self {
        self.push("Bar".to_string());
        self
    }
}

fn main() {
    // You can optionally experiment here.
}

#[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(), "Bar");
        assert_eq!(foo.pop().unwrap(), "Foo");
    }
}
```

* This exercise also similar like the first one.
* We need to create implementation of `AppendBar` trait for `Vec<String>`
* Because we want to mutate the vector (`self`) we need to add `mut` syntax.
* The full implementation will look like this:

  ```rust
  impl AppendBar for Vec<String> {
      fn append_bar(mut self) -> Self {
          self.push("Bar".to_string());
          self
      }
  }
  ```

## traits3.rs

```rust
trait Licensed {
    // Add a default implementation for `licensing_info` so that
    // implementors like the two structs below can share that default behavior
    // without repeating the function.
    // The default license information should be the string "Default license".
    fn licensing_info(&self) -> String {
        "Default license".to_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.

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn is_licensing_info_the_same() {
        let licensing_info = "Default license";
        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);
    }
}
```

* In this exercise we need to create default implementation of `licensing_info` that will return `"Default license"`.
* This default implementation will be called if not the trait is not implemented for the specified type.

## traits4.rs

```rust
trait Licensed {
    fn licensing_info(&self) -> String {
        "Default license".to_string()
    }
}

struct SomeSoftware;
struct OtherSoftware;

impl Licensed for SomeSoftware {}
impl Licensed for OtherSoftware {}

// Fix the compiler error by only changing the signature of this function.
fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool {
    software1.licensing_info() == software2.licensing_info()
}

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn compare_license_information() {
        assert!(compare_license_types(SomeSoftware, OtherSoftware));
    }

    #[test]
    fn compare_license_information_backwards() {
        assert!(compare_license_types(OtherSoftware, SomeSoftware));
    }
}
```

* In this exercise we need to change the function signature of `compare_license_types` to accept both `SomeSoftware` and `OtherSoftware` type.
* We can do this using generics.
* Or we can also use `impl Trait` syntax like this:

  ```rust
  fn compare_license_types(software1: impl Licensed, software2: impl Licensed) -> bool
  ```
* You can read more about it in here: <https://doc.rust-lang.org/book/ch10-02-traits.html#trait-bound-syntax>

## traits5.rs

```rust
trait SomeTrait {
    fn some_function(&self) -> bool {
        true
    }
}

trait OtherTrait {
    fn other_function(&self) -> bool {
        true
    }
}

struct SomeStruct;
impl SomeTrait for SomeStruct {}
impl OtherTrait for SomeStruct {}

struct OtherStruct;
impl SomeTrait for OtherStruct {}
impl OtherTrait for OtherStruct {}

// Fix the compiler error by only changing the signature of this function.
fn some_func(item: (impl SomeTrait + OtherTrait)) -> bool {
    item.some_function() && item.other_function()
}

fn main() {
    // You can optionally experiment here.
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_some_func() {
        assert!(some_func(SomeStruct));
        assert!(some_func(OtherStruct));
    }
}
```

* In this exercise we need to update function signature of `some_func` to accept implementation of `SomeTrait` and `OtherTrait`.
* It means that given type must implement both trait.
* We can do this by using `+` syntax like this:

  ```rust
  fn some_func(item: (impl SomeTrait + OtherTrait)) -> bool
  ```
