Skip to content

Provide example of generic inverse() #21109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 16, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/doc/trpl/generics.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,9 @@ enum Result<H, 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:
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);
Expand Down
73 changes: 73 additions & 0 deletions src/doc/trpl/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,3 +315,76 @@ The names don't actually change to this, it's just for illustration. But
as you can see, there's no overhead of deciding which version to call here,
hence *statically dispatched*. The downside is that we have two copies of
the same function, so our binary is a little bit larger.

## Our `inverse` Example

Back in [Generics](generics.html), we were trying to write code like this:

```{rust,ignore}
fn inverse<T>(x: T) -> Result<T, String> {
if x == 0.0 { return Err("x cannot be zero!".to_string()); }

Ok(1.0 / x)
}
```

If we try to compile it, we get this error:

```text
error: binary operation `==` cannot be applied to type `T`
```

This is because `T` is too generic: we don't know if a random `T` can be
compared. For that, we can use trait bounds. It doesn't quite work, but try
this:

```{rust,ignore}
fn inverse<T: PartialEq>(x: T) -> Result<T, String> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that we're conventionally recommending Eq as a bound for generics rather than PartialEq

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh? But floats don't have Eq...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh right! Perhaps integers could be used? Integers are the ones that panic on divide-by-zero as well.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How big of a deal is it? I ask because this isn't just here, I'd have to go back and revise the other section. Which is fine, but maybe just mentioning how floats are a good example of the Eq / PartialEq difference is worth it? Or maybe just show Eq and the different error message?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think keeping PartialEq with the float is fine.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah oh well, it's not worth that much trouble, PartialEq is fine.

if x == 0.0 { return Err("x cannot be zero!".to_string()); }

Ok(1.0 / x)
}
```

You should get this error:

```text
error: mismatched types:
expected `T`,
found `_`
(expected type parameter,
found floating-point variable)
```

So this won't work. While our `T` is `PartialEq`, we expected to have another `T`,
but instead, we found a floating-point variable. We need a different bound. `Float`
to the rescue:

```
use std::num::Float;

fn inverse<T: Float>(x: T) -> Result<T, String> {
if x == Float::zero() { return Err("x cannot be zero!".to_string()) }

let one: T = Float::one();
Ok(one / x)
}
```

We've had to replace our generic `0.0` and `1.0` with the appropriate methods
from the `Float` trait. Both `f32` and `f64` implement `Float`, so our function
works just fine:

```
# use std::num::Float;
# fn inverse<T: Float>(x: T) -> Result<T, String> {
# if x == Float::zero() { return Err("x cannot be zero!".to_string()) }
# let one: T = Float::one();
# Ok(one / x)
# }
println!("the inverse of {} is {:?}", 2.0f32, inverse(2.0f32));
println!("the inverse of {} is {:?}", 2.0f64, inverse(2.0f64));

println!("the inverse of {} is {:?}", 0.0f32, inverse(0.0f32));
println!("the inverse of {} is {:?}", 0.0f64, inverse(0.0f64));
```