Improve `Output` explanation.

This commit is contained in:
LukeMathWalker 2024-05-24 12:55:58 +02:00
parent cbafcf2cd4
commit eb0b4f75f0
1 changed files with 35 additions and 2 deletions

View File

@ -81,6 +81,11 @@ impl Add<u32> for u32 {
type Output = u32; type Output = u32;
fn add(self, rhs: u32) -> u32 { fn add(self, rhs: u32) -> u32 {
// ^^^
// This could be written as `Self::Output` instead.
// The compiler doesn't care, as long as the type you
// specify here matches the type you assigned to `Output`
// right above.
// [...] // [...]
} }
} }
@ -104,8 +109,31 @@ because `u32` implements `Add<&u32>` _as well as_ `Add<u32>`.
### `Output` ### `Output`
`Output`, on the other hand, **must** be uniquely determined once the types of the operands `Output` represents the type of the result of the addition.
are known. That's why it's an associated type instead of a second generic parameter.
Why do we need `Output` in the first place? Can't we just use `Self` as output, the type implementing `Add`?
We could, but it would limit the flexibility of the trait. In the standard library, for example, you'll find
this implementation:
```rust
impl Add<&u32> for &u32 {
type Output = u32;
fn add(self, rhs: &u32) -> u32 {
// [...]
}
}
```
The type they're implementing the trait for is `&u32`, but the result of the addition is `u32`.
It would be impossible[^flexible] to provide this implementation if `add` had to return `Self`, i.e. `&u32` in this case.
`Output` lets `std` decouple the implementor from the return type, thus supporting this case.
On the other hand, `Output` can't be a generic parameter. The output type of the operation **must** be uniquely determined
once the types of the operands are known. That's why it's an associated type: for a given combination of implementor
and generic parameters, there is only one `Output` type.
## Conclusion
To recap: To recap:
@ -116,3 +144,8 @@ To recap:
## References ## References
- The exercise for this section is located in `exercises/04_traits/10_assoc_vs_generic` - The exercise for this section is located in `exercises/04_traits/10_assoc_vs_generic`
[^flexible]: Flexibility is rarely free: the trait definition is more complex due to `Output`, and implementors have to reason about
what they want to return. The trade-off is only justified if that flexibility is actually needed. Keep that in mind
when designing your own traits.