@@ -145,9 +145,9 @@ impl Hash for i64 {
145
145
}
146
146
```
147
147
148
- Unlike interfaces in languages like Java, C# or Scala, new traits can be implemented
149
- for existing types (as with ` Hash ` above). ** Code in upstream crates can be
150
- adapted to interfaces in downstream crates ** .
148
+ Unlike interfaces in languages like Java, C# or Scala, ** new traits can be
149
+ implemented for existing types** (as with ` Hash ` above). That means abstractions
150
+ can be created after-the-fact, and applied to existing libraries .
151
151
152
152
Unlike inherent methods, trait methods are in scope only when their trait
153
153
is. But assuming ` Hash ` is in scope, you can write ` true.hash() ` , so
@@ -181,7 +181,13 @@ C++ templates, the compiler will generate *two copies* of the `print_hash`
181
181
method to handle the above code, one for each concrete argument type. That in
182
182
turn means that the internal call to ` t.hash() ` -- the point where the
183
183
abstraction is actually used -- has zero cost: it will be compiled to a direct,
184
- static call to the relevant implementation.
184
+ static call to the relevant implementation:
185
+
186
+ ``` rust
187
+ // The compiled code:
188
+ __print_hash_bool (& true ); // invoke specialized bool version directly
189
+ __print_hash_i64 (& true ); // invoke specialized i64 version directly
190
+ ```
185
191
186
192
This compilation model isn't so useful for a function like ` print_hash ` , but
187
193
it's * very* useful for more realistic uses of hashing. Suppose we also introduce
@@ -214,6 +220,7 @@ The static compilation model for generics will then yield several benefits:
214
220
there is no extra cost dispatching to calls to ` hash ` and ` eq ` , as above. It
215
221
also means that the optimizer gets to work with the fully concrete code --
216
222
that is, from the point of view of the optimizer, * there is no abstraction* .
223
+ In particular, static dispatch allows for * inlining* across uses of generics.
217
224
218
225
Altogether, just as in C++ templates, these aspects of generics mean that you
219
226
can write quite high-level abstractions that are * guaranteed* to compile down to
@@ -226,36 +233,6 @@ rather than being checked repeatedly when applied to concrete types. That means
226
233
earlier, clearer compilation errors for library authors, and less typechecking
227
234
overhead for clients.
228
235
229
- #### Conditional trait implementation
230
-
231
- Generics also make it possible to implement a trait * conditionally* :
232
-
233
- ``` rust
234
- struct Pair <A , B > {
235
- first : A ,
236
- second : B ,
237
- }
238
-
239
- impl <A : Hash , B : Hash > Hash for Pair <A , B > {
240
- fn hash (& self ) -> u64 {
241
- self . first. hash ()
242
- . xor (self . second. hash ())
243
- . wrapping_mul (0x100000001b3 );
244
- }
245
- }
246
- ```
247
-
248
- Here, the ` Pair ` type implements ` Hash ` if, and only if, its components
249
- do. Conditional implementation enables flexible reuse of code: it would be a
250
- shame to have to introduce separate ` Pair ` types that varied by the traits their
251
- components implement. It's such a common pattern in Rust that there is built-in
252
- support for generating certain kinds of "mechanical" implementations automatically:
253
-
254
- ``` rust
255
- #[derive(Hash )]
256
- struct Pair <A , B > { .. }
257
- ```
258
-
259
236
### Dynamic dispatch
260
237
261
238
We've seen one compilation model for traits, where all abstraction is compiled
@@ -330,6 +307,32 @@ wind up playing a few other important roles in Rust. Here's a taste:
330
307
simply particular traits. You can read more about how this works in
331
308
Huon Wilson's [ in-depth post] [ closures ] on the topic.
332
309
310
+ * ** Conditional APIs** . Generics make it possible to implement a trait
311
+ conditionally:
312
+
313
+ ``` rust
314
+ struct Pair <A , B > { first : A , second : B }
315
+
316
+ impl <A : Hash , B : Hash > Hash for Pair <A , B > {
317
+ fn hash (& self ) -> u64 {
318
+ self . first. hash ()
319
+ . xor (self . second. hash ())
320
+ . wrapping_mul (0x100000001b3 );
321
+ }
322
+ }
323
+ ```
324
+
325
+ Here, the ` Pair ` type implements ` Hash ` if, and only if, its components do --
326
+ allowing the single ` Pair ` type to be used in different contexts, while
327
+ supporting the largest API available for each context. It's such a common
328
+ pattern in Rust that there is built-in support for generating certain kinds of
329
+ "mechanical" implementations automatically:
330
+
331
+ ``` rust
332
+ #[derive(Hash )]
333
+ struct Pair <A , B > { .. }
334
+ ```
335
+
333
336
* ** Extension methods** . Traits can be used to extend an existing type (defined
334
337
elsewhere) with new methods, for convenience, similarly to C#'s extension
335
338
methods. This falls directly out of the scoping rules for traits: you just
0 commit comments