Rust by Example

28 Bounds

When working with generics, the type parameters (e.g. Ty) may use traits (e.g. Tr) as bounds (e.g. Ty: Tr, which reads as: Ty must implement the Tr trait). Bounding has two effects:

  • Generics instances (let ty: Ty = (...)) can now access the methods (ty.tr()) of the traits specified in the bounds.
  • The generic can only be specialized for type parameters that conform to the bounds.

Bounds are typically applied in one of two ways:

  • At the first instance of the type
  • In a where clause which directly precedes the { in the impl
use std::ops::{Add, Sub, Mul}; #[derive(Debug, Copy)] struct Vec2<T> { x: T, y: T, } // Apply bound to `T` at first instance of `T`. `T` // must implement the `Add` trait. impl<T: Add<T, Output = T>> Add<Vec2<T>> for Vec2<T> { type Output = Vec2<T>; fn add(self, rhs: Vec2<T>) -> Vec2<T> { Vec2 { // `x` and `y` are of type `T`, and implement the `add` method x: self.x.add(rhs.x), // The sugary `+` operator can also be used y: self.y + rhs.y, } } } // Bound: `T` must implement the `Sub` trait impl<T> Sub<Vec2<T>> for Vec2<T> where T: Sub<T, Output = T> { type Output = Vec2<T>; fn sub(self, rhs: Vec2<T>) -> Vec2<T> { Vec2 { x: self.x - rhs.x, y: self.y - rhs.y, } } } // Bound: `T` must implement *both* the `Add` trait and the `Mul` trait impl<T> Vec2<T> where T: Add<T, Output = T> + Mul<T, Output = T> { fn dot(self, rhs: Vec2<T>) -> T { (self.x * rhs.x) + (self.y * rhs.y) } } fn main() { // Floats implement the `Add`, `Mul` and `Sub` traits let v1 = Vec2 { x: 1.2_f32, y: 3.4 }; let v2 = Vec2 { x: 5.6_f32, y: 7.8 }; println!("{:?} + {:?} = {:?}", v1, v2, v1 + v2); println!("{:?} - {:?} = {:?}", v1, v2, v1 - v2); println!("{:?} ⋅ {:?} = {:?}", v1, v2, v1.dot(v2)); // Error! `char` doesn't implement the `Add` trait println!("{:?}", Vec2 { x: ' ', y: 'b' } + Vec2 { x: 'c', y: 'd' }); // FIXME ^ Comment out this line }