about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/doc/guide.md176
1 files changed, 176 insertions, 0 deletions
diff --git a/src/doc/guide.md b/src/doc/guide.md
index c00020a8c00..69377b1185b 100644
--- a/src/doc/guide.md
+++ b/src/doc/guide.md
@@ -3620,6 +3620,182 @@ guide](http://doc.rust-lang.org/guide-pointers.html#rc-and-arc).
 
 # 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?
+
+```{rust}
+enum OptionalInt {
+    Value(int),
+    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:
+
+```rust
+enum Option<T> {
+    Some(T),
+    None,
+}
+```
+
+The `<T>` 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
+example of using `Option<T>`, with some extra type annotations:
+
+```{rust}
+let x: Option<int> = Some(5i);
+```
+
+In the type declaration, we say `Option<int>`. Note how similar this looks to
+`Option<T>`. So, in this particular `Option`, `T` has the value of `int`. On
+the right hand side of the binding, we do make a `Some(T)`, where `T` is `5i`.
+Since that's an `int`, the two sides match, and Rust is happy. If they didn't
+match, we'd get an error:
+
+```{rust,ignore}
+let x: Option<f64> = Some(5i);
+// error: mismatched types: expected `core::option::Option<f64>`
+// but found `core::option::Option<int>` (expected f64 but found int)
+```
+
+That doesn't mean we can't make `Option<T>`s that hold an `f64`! They just have to
+match up:
+
+```{rust}
+let x: Option<int> = Some(5i);
+let y: Option<f64> = 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<T, E>` type:
+
+```{rust}
+enum Result<T, E> {
+    Ok(T),
+    Err(E),
+}
+```
+
+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<T, E>` as:
+
+```{rust}
+enum Result<H, N> {
+    Ok(H),
+    Err(N),
+}
+```
+
+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.
+
+The `Result<T, E>` 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. Here's an example:
+
+```{rust}
+let x: Result<f64, String> = Ok(2.3f64);
+let y: Result<f64, String> = Err("There was an error.".to_string());
+```
+
+This particular Result will return an `int` if there's a success, and a
+`String` if there's a failure. Let's write a function that uses `Result<T, E>`:
+
+```{rust}
+fn square_root(x: f64) -> Result<f64, String> {
+    if x < 0.0f64 { return Err("x must be positive!".to_string()); }
+
+    Ok(x * (1.0f64 / 2.0f64))
+}
+```
+
+We don't want to take the square root of a negative number, so we check
+to make sure that's true. If it's not, then we return an `Err`, with a
+message. If it's okay, we return an `Ok`, with the answer.
+
+Why does this matter? Well, remember how `match` does exhaustive matches?
+Here's how this function gets used:
+
+```{rust}
+# fn square_root(x: f64) -> Result<f64, String> {
+#     if x < 0.0f64 { return Err("x must be positive!".to_string()); }
+#     Ok(x * (1.0f64 / 2.0f64))
+# }
+let x = square_root(25.0f64);
+
+match x {
+    Ok(x) => println!("The square root of 25 is {}", x),
+    Err(msg) => println!("Error: {}", msg),
+}
+```
+
+The `match enforces that we handle the `Err` case. In addition, because the
+answer is wrapped up in an `Ok`, we can't just use the result without doing
+the match:
+
+```{rust,ignore}
+let x = square_root(25.0f64);
+println!("{}", x + 2.0f64); // error: binary operation `+` cannot be applied 
+           // to type `core::result::Result<f64,collections::string::String>`
+```
+
+This function is great, but there's one other problem: it only works for 64 bit
+floating point values. What if we wanted to handle 32 bit floating point as
+well? We'd have to write this:
+
+```{rust}
+fn square_root32(x: f32) -> Result<f32, String> {
+    if x < 0.0f32 { return Err("x must be positive!".to_string()); }
+
+    Ok(x * (1.0f32 / 2.0f32))
+}
+```
+
+Bummer. What we need is a **generic function**. Luckily, we can write one!
+However, it won't _quite_ work yet. Before we get into that, let's talk syntax.
+A generic version of `square_root` would look something like this:
+
+```{rust,ignore}
+fn square_root<T>(x: T) -> Result<T, String> {
+    if x < 0.0 { return Err("x must be positive!".to_string()); }
+
+    Ok(x * (1.0 / 2.0))
+}
+```
+
+Just like how we had `Option<T>`, we use a similar syntax for `square_root<T>`.
+We can then use `T` inside the rest of the signature: `x` has type `T`, and half
+of the `Result` has type `T`. However, if we try to compile that example, we'll get
+an error:
+
+```{notrust,ignore}
+error: binary operation `<` cannot be applied to type `T`
+```
+
+Because `T` can be _any_ type, it may be a type that doesn't implement `<`,
+and therefore, the first line would be wrong. What do we do?
+
+To fix this example, we need to learn about another Rust feature: traits.
+
 # Traits
 
 # Operators and built-in Traits