Trait Bounds

When working with generics, you often want to require the types to implement some trait, so that you can call this trait’s methods.

You can do this with T: Trait:

1. ```
   fn duplicate<T: Clone>(a: T) -> (T, T) {
   ```
2. ```
       (a.clone(), a.clone())
   ```
3. ```
   }
   ```

5. ```
   // struct NotCloneable;
   ```

7. ```
   fn main() {
   ```
8. ```
       let foo = String::from("foo");
   ```
9. ```
       let pair = duplicate(foo);
   ```
10. ```
        println!("{pair:?}");
    ```
11. ```
    }
    ```

This slide should take about 8 minutes.

  • Try making a NonCloneable and passing it to duplicate.
  • When multiple traits are necessary, use + to join them.
  • Show a where clause, students will encounter it when reading code.

1. fn duplicate<T>(a: T) -> (T, T) 2. where 3. T: Clone, 4. { 5. (a.clone(), a.clone()) 6. }

  • It declutters the function signature if you have many parameters.
  • It has additional features making it more powerful.
    • If someone asks, the extra feature is that the type on the left of “:” can be arbitrary, like Option<T>.
  • Note that Rust does not (yet) support specialization. For example, given the original duplicate, it is invalid to add a specialized duplicate(a: u32).