diff --git a/src/doc/trpl/generics.md b/src/doc/trpl/generics.md index b13c68c45c8..517a6e60642 100644 --- a/src/doc/trpl/generics.md +++ b/src/doc/trpl/generics.md @@ -1,31 +1,13 @@ % Generics Sometimes, when writing a function or data type, we may want it to work for -multiple types of arguments. For example, remember our `OptionalInt` type? +multiple types of arguments. Luckily, Rust has a feature that gives us a better +way: generics. Generics are called ‘parametric polymorphism’ in type theory, +which means that they are types or functions that have multiple forms (‘poly’ +is multiple, ‘morph’ is form) over a given parameter (‘parametric’). -```{rust} -enum OptionalInt { - Value(i32), - Missing, -} -``` - -If we wanted to also have an `OptionalFloat64`, we would need a new enum: - -```{rust} -enum OptionalFloat64 { - Valuef64(f64), - Missingf64, -} -``` - -This is really unfortunate. Luckily, Rust has a feature that gives us a better -way: generics. Generics are called *parametric polymorphism* in type theory, -which means that they are types or functions that have multiple forms (*poly* -is multiple, *morph* is form) over a given parameter (*parametric*). - -Anyway, enough with type theory declarations, let's check out the generic form -of `OptionalInt`. It is actually provided by Rust itself, and looks like this: +Anyway, enough with type theory, let’s check out some generic code. Rust’s +standard library provides a type, `Option`, that’s generic: ```rust enum Option { @@ -34,41 +16,40 @@ enum Option { } ``` -The `` part, which you've seen a few times before, indicates that this is +The `` part, which you’ve seen a few times before, indicates that this is a generic data type. Inside the declaration of our enum, wherever we see a `T`, -we substitute that type for the same type used in the generic. Here's an +we substitute that type for the same type used in the generic. Here’s an example of using `Option`, with some extra type annotations: -```{rust} +```rust let x: Option = Some(5); ``` In the type declaration, we say `Option`. Note how similar this looks to `Option`. So, in this particular `Option`, `T` has the value of `i32`. On the right-hand side of the binding, we do make a `Some(T)`, where `T` is `5`. -Since that's an `i32`, the two sides match, and Rust is happy. If they didn't -match, we'd get an error: +Since that’s an `i32`, the two sides match, and Rust is happy. If they didn’t +match, we’d get an error: -```{rust,ignore} +```rust,ignore let x: Option = Some(5); // error: mismatched types: expected `core::option::Option`, // found `core::option::Option<_>` (expected f64 but found integral variable) ``` -That doesn't mean we can't make `Option`s that hold an `f64`! They just have to -match up: +That doesn’t mean we can’t make `Option`s that hold an `f64`! They just have +to match up: -```{rust} +```rust let x: Option = Some(5); let y: Option = Some(5.0f64); ``` This is just fine. One definition, multiple uses. -Generics don't have to only be generic over one type. Consider Rust's built-in -`Result` type: +Generics don’t have to only be generic over one type. Consider another type from Rust’s standard library that’s similar, `Result`: -```{rust} +```rust enum Result { Ok(T), Err(E), @@ -76,9 +57,9 @@ enum Result { ``` This type is generic over _two_ types: `T` and `E`. By the way, the capital letters -can be any letter you'd like. We could define `Result` as: +can be any letter you’d like. We could define `Result` as: -```{rust} +```rust enum Result { Ok(A), Err(Z), @@ -86,7 +67,58 @@ enum Result { ``` if we wanted to. Convention says that the first generic parameter should be -`T`, for 'type,' and that we use `E` for 'error.' Rust doesn't care, however. +`T`, for ‘type’, and that we use `E` for ‘error’. Rust doesn’t care, however. The `Result` type is intended to be used to return the result of a -computation, and to have the ability to return an error if it didn't work out. +computation, and to have the ability to return an error if it didn’t work out. + +## Generic functions + +We can write functions that take generic types with a similar syntax: + +```rust +fn takes_anything(x: T) { + // do something with x +} +``` + +The syntax has two parts: the `` says “this function is generic over one +type, `T`”, and the `x: T` says “x has the type `T`.” + +Multiple arguments can have the same generic type: + +```rust +fn takes_two_of_the_same_things(x: T, y: T) { + // ... +} +``` + +We could write a version that takes multiple types: + +```rust +fn takes_two_things(x: T, y: U) { + // ... +} +``` + +Generic functions are most useful with ‘trait bounds’, which we’ll cover in the +[section on traits][traits]. + +[traits]: traits.html + +## Generic structs + +You can store a generic type in a `struct` as well: + +``` +struct Point { + x: T, + y: T, +} + +let int_origin = Point { x: 0, y: 0 }; +let float_origin = Point { x: 0.0, y: 0.0 }; +``` + +Similarly to functions, the `` is where we declare the generic parameters, +and we then use `x: T` in the type declaration, too.