Skip to content

Commit 057c9ae

Browse files
committed
Guide: generics
1 parent efe1f7e commit 057c9ae

File tree

1 file changed

+176
-0
lines changed

1 file changed

+176
-0
lines changed

src/doc/guide.md

Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3620,6 +3620,182 @@ guide](http://doc.rust-lang.org/guide-pointers.html#rc-and-arc).
36203620

36213621
# Generics
36223622

3623+
Sometimes, when writing a function or data type, we may want it to work for
3624+
multiple types of arguments. For example, remember our `OptionalInt` type?
3625+
3626+
```{rust}
3627+
enum OptionalInt {
3628+
Value(int),
3629+
Missing,
3630+
}
3631+
```
3632+
3633+
If we wanted to also have an `OptionalFloat64`, we would need a new enum:
3634+
3635+
```{rust}
3636+
enum OptionalFloat64 {
3637+
Valuef64(f64),
3638+
Missingf64,
3639+
}
3640+
```
3641+
3642+
This is really unfortunate. Luckily, Rust has a feature that gives us a better
3643+
way: generics. Generics are called **parametric polymorphism** in type theory,
3644+
which means that they are types or functions that have multiple forms ("poly"
3645+
is multiple, "morph" is form) over a given parameter ("parametric").
3646+
3647+
Anyway, enough with type theory declarations, let's check out the generic form
3648+
of `OptionalInt`. It is actually provided by Rust itself, and looks like this:
3649+
3650+
```rust
3651+
enum Option<T> {
3652+
Some(T),
3653+
None,
3654+
}
3655+
```
3656+
3657+
The `<T>` part, which you've seen a few times before, indicates that this is
3658+
a generic data type. Inside the declaration of our enum, wherever we see a `T`,
3659+
we substitute that type for the same type used in the generic. Here's an
3660+
example of using `Option<T>`, with some extra type annotations:
3661+
3662+
```{rust}
3663+
let x: Option<int> = Some(5i);
3664+
```
3665+
3666+
In the type declaration, we say `Option<int>`. Note how similar this looks to
3667+
`Option<T>`. So, in this particular `Option`, `T` has the value of `int`. On
3668+
the right hand side of the binding, we do make a `Some(T)`, where `T` is `5i`.
3669+
Since that's an `int`, the two sides match, and Rust is happy. If they didn't
3670+
match, we'd get an error:
3671+
3672+
```{rust,ignore}
3673+
let x: Option<f64> = Some(5i);
3674+
// error: mismatched types: expected `core::option::Option<f64>`
3675+
// but found `core::option::Option<int>` (expected f64 but found int)
3676+
```
3677+
3678+
That doesn't mean we can't make `Option<T>`s that hold an `f64`! They just have to
3679+
match up:
3680+
3681+
```{rust}
3682+
let x: Option<int> = Some(5i);
3683+
let y: Option<f64> = Some(5.0f64);
3684+
```
3685+
3686+
This is just fine. One definition, multiple uses.
3687+
3688+
Generics don't have to only be generic over one type. Consider Rust's built-in
3689+
`Result<T, E>` type:
3690+
3691+
```{rust}
3692+
enum Result<T, E> {
3693+
Ok(T),
3694+
Err(E),
3695+
}
3696+
```
3697+
3698+
This type is generic over _two_ types: `T` and `E`. By the way, the capital letters
3699+
can be any letter you'd like. We could define `Result<T, E>` as:
3700+
3701+
```{rust}
3702+
enum Result<H, N> {
3703+
Ok(H),
3704+
Err(N),
3705+
}
3706+
```
3707+
3708+
if we wanted to. Convention says that the first generic parameter should be
3709+
`T`, for 'type,' and that we use `E` for 'error.' Rust doesn't care, however.
3710+
3711+
The `Result<T, E>` type is intended to
3712+
be used to return the result of a computation, and to have the ability to
3713+
return an error if it didn't work out. Here's an example:
3714+
3715+
```{rust}
3716+
let x: Result<f64, String> = Ok(2.3f64);
3717+
let y: Result<f64, String> = Err("There was an error.".to_string());
3718+
```
3719+
3720+
This particular Result will return an `int` if there's a success, and a
3721+
`String` if there's a failure. Let's write a function that uses `Result<T, E>`:
3722+
3723+
```{rust}
3724+
fn square_root(x: f64) -> Result<f64, String> {
3725+
if x < 0.0f64 { return Err("x must be positive!".to_string()); }
3726+
3727+
Ok(x * (1.0f64 / 2.0f64))
3728+
}
3729+
```
3730+
3731+
We don't want to take the square root of a negative number, so we check
3732+
to make sure that's true. If it's not, then we return an `Err`, with a
3733+
message. If it's okay, we return an `Ok`, with the answer.
3734+
3735+
Why does this matter? Well, remember how `match` does exhaustive matches?
3736+
Here's how this function gets used:
3737+
3738+
```{rust}
3739+
# fn square_root(x: f64) -> Result<f64, String> {
3740+
# if x < 0.0f64 { return Err("x must be positive!".to_string()); }
3741+
# Ok(x * (1.0f64 / 2.0f64))
3742+
# }
3743+
let x = square_root(25.0f64);
3744+
3745+
match x {
3746+
Ok(x) => println!("The square root of 25 is {}", x),
3747+
Err(msg) => println!("Error: {}", msg),
3748+
}
3749+
```
3750+
3751+
The `match enforces that we handle the `Err` case. In addition, because the
3752+
answer is wrapped up in an `Ok`, we can't just use the result without doing
3753+
the match:
3754+
3755+
```{rust,ignore}
3756+
let x = square_root(25.0f64);
3757+
println!("{}", x + 2.0f64); // error: binary operation `+` cannot be applied
3758+
// to type `core::result::Result<f64,collections::string::String>`
3759+
```
3760+
3761+
This function is great, but there's one other problem: it only works for 64 bit
3762+
floating point values. What if we wanted to handle 32 bit floating point as
3763+
well? We'd have to write this:
3764+
3765+
```{rust}
3766+
fn square_root32(x: f32) -> Result<f32, String> {
3767+
if x < 0.0f32 { return Err("x must be positive!".to_string()); }
3768+
3769+
Ok(x * (1.0f32 / 2.0f32))
3770+
}
3771+
```
3772+
3773+
Bummer. What we need is a **generic function**. Luckily, we can write one!
3774+
However, it won't _quite_ work yet. Before we get into that, let's talk syntax.
3775+
A generic version of `square_root` would look something like this:
3776+
3777+
```{rust,ignore}
3778+
fn square_root<T>(x: T) -> Result<T, String> {
3779+
if x < 0.0 { return Err("x must be positive!".to_string()); }
3780+
3781+
Ok(x * (1.0 / 2.0))
3782+
}
3783+
```
3784+
3785+
Just like how we had `Option<T>`, we use a similar syntax for `square_root<T>`.
3786+
We can then use `T` inside the rest of the signature: `x` has type `T`, and half
3787+
of the `Result` has type `T`. However, if we try to compile that example, we'll get
3788+
an error:
3789+
3790+
```{notrust,ignore}
3791+
error: binary operation `<` cannot be applied to type `T`
3792+
```
3793+
3794+
Because `T` can be _any_ type, it may be a type that doesn't implement `<`,
3795+
and therefore, the first line would be wrong. What do we do?
3796+
3797+
To fix this example, we need to learn about another Rust feature: traits.
3798+
36233799
# Traits
36243800

36253801
# Operators and built-in Traits

0 commit comments

Comments
 (0)