mirror of
https://github.com/mainmatter/100-exercises-to-learn-rust
synced 2024-12-25 21:58:26 +01:00
Improve Output
explanation.
This commit is contained in:
parent
cbafcf2cd4
commit
eb0b4f75f0
1 changed files with 35 additions and 2 deletions
|
@ -81,6 +81,11 @@ impl Add<u32> for u32 {
|
|||
type Output = 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`, on the other hand, **must** be uniquely determined once the types of the operands
|
||||
are known. That's why it's an associated type instead of a second generic parameter.
|
||||
`Output` represents the type of the result of the addition.
|
||||
|
||||
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:
|
||||
|
||||
|
@ -116,3 +144,8 @@ To recap:
|
|||
## References
|
||||
|
||||
- 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.
|
||||
|
||||
|
|
Loading…
Reference in a new issue