| # Testcase: unit clarification |
| |
| A useful method of unit conversions can be examined by implementing `Add` |
| with a phantom type parameter. The `Add` `trait` is examined below: |
| |
| ```rust,ignore |
| // This construction would impose: `Self + RHS = Output` |
| // where RHS defaults to Self if not specified in the implementation. |
| pub trait Add<RHS = Self> { |
| type Output; |
| |
| fn add(self, rhs: RHS) -> Self::Output; |
| } |
| |
| // `Output` must be `T<U>` so that `T<U> + T<U> = T<U>`. |
| impl<U> Add for T<U> { |
| type Output = T<U>; |
| ... |
| } |
| ``` |
| |
| The whole implementation: |
| |
| ```rust,editable |
| use std::ops::Add; |
| use std::marker::PhantomData; |
| |
| /// Create void enumerations to define unit types. |
| #[derive(Debug, Clone, Copy)] |
| enum Inch {} |
| #[derive(Debug, Clone, Copy)] |
| enum Mm {} |
| |
| /// `Length` is a type with phantom type parameter `Unit`, |
| /// and is not generic over the length type (that is `f64`). |
| /// |
| /// `f64` already implements the `Clone` and `Copy` traits. |
| #[derive(Debug, Clone, Copy)] |
| struct Length<Unit>(f64, PhantomData<Unit>); |
| |
| /// The `Add` trait defines the behavior of the `+` operator. |
| impl<Unit> Add for Length<Unit> { |
| type Output = Length<Unit>; |
| |
| // add() returns a new `Length` struct containing the sum. |
| fn add(self, rhs: Length<Unit>) -> Length<Unit> { |
| // `+` calls the `Add` implementation for `f64`. |
| Length(self.0 + rhs.0, PhantomData) |
| } |
| } |
| |
| fn main() { |
| // Specifies `one_foot` to have phantom type parameter `Inch`. |
| let one_foot: Length<Inch> = Length(12.0, PhantomData); |
| // `one_meter` has phantom type parameter `Mm`. |
| let one_meter: Length<Mm> = Length(1000.0, PhantomData); |
| |
| // `+` calls the `add()` method we implemented for `Length<Unit>`. |
| // |
| // Since `Length` implements `Copy`, `add()` does not consume |
| // `one_foot` and `one_meter` but copies them into `self` and `rhs`. |
| let two_feet = one_foot + one_foot; |
| let two_meters = one_meter + one_meter; |
| |
| // Addition works. |
| println!("one foot + one_foot = {:?} in", two_feet.0); |
| println!("one meter + one_meter = {:?} mm", two_meters.0); |
| |
| // Nonsensical operations fail as they should: |
| // Compile-time Error: type mismatch. |
| //let one_feter = one_foot + one_meter; |
| } |
| ``` |
| |
| ### See also: |
| |
| [Borrowing (`&`)], [Bounds (`X: Y`)], [enum], [impl & self], |
| [Overloading], [ref], [Traits (`X for Y`)], and [TupleStructs]. |
| |
| [Borrowing (`&`)]: ../../scope/borrow.md |
| [Bounds (`X: Y`)]: ../../generics/bounds.md |
| [enum]: ../../custom_types/enum.md |
| [impl & self]: ../../fn/methods.md |
| [Overloading]: ../../trait/ops.md |
| [ref]: ../../scope/borrow/ref.md |
| [Traits (`X for Y`)]: ../../trait.md |
| [TupleStructs]: ../../custom_types/structs.md |
| [std::marker::PhantomData]: https://doc.rust-lang.org/std/marker/struct.PhantomData.html |