Skip to content

Document where clauses. #22123

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
Feb 18, 2015
Merged
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
90 changes: 90 additions & 0 deletions src/doc/trpl/traits.md
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,96 @@ One last thing about traits: generic functions with a trait bound use
dispatched. What's that mean? Check out the chapter on [static and dynamic
dispatch](static-and-dynamic-dispatch.html) for more.

## Where clause

Writing functions with only a few generic types and a small number of trait
bounds isn't too bad, but as the number increases, the syntax gets increasingly
awkward:

```
use std::fmt::Debug;

fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
x.clone();
y.clone();
println!("{:?}", y);
}
```

The name of the function is on the far left, and the parameter list is on the
far right. The bounds are getting in the way.

Rust has a solution, and it's called a '`where` clause':

```
use std::fmt::Debug;

fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
x.clone();
y.clone();
println!("{:?}", y);
}

fn bar<T, K>(x: T, y: K) where T: Clone, K: Clone + Debug {
x.clone();
y.clone();
println!("{:?}", y);
}

fn main() {
foo("Hello", "world");
bar("Hello", "workd");
}
```

`foo()` uses the syntax we showed earlier, and `bar()` uses a `where` clause.
All you need to do is leave off the bounds when defining your type parameters,
and then add `where` after the parameter list. For longer lists, whitespace can
be added:

```
use std::fmt::Debug;

fn bar<T, K>(x: T, y: K)
where T: Clone,
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we have a set style for this? The most common I've seen is:

fn bar<T, K>(x: T, y: K)
where T: Clone, K: Clone + Debug {
     ..
}

and/or

fn bar<T, K>(x: T, y: K)
where T: Clone, 
      K: Clone + Debug {
     ..
}

Copy link
Contributor

Choose a reason for hiding this comment

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

This was also asked at http://internals.rust-lang.org/t/multiple-line-fn-and-impl-declarations/1106 but no one responded. His preference was 2 tab indentation for where.

fn bar<T, K>(x: T, y: K)
        where T: Clone, 
    K: Clone + Debug {
     ..
}

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah we don't have one so I did what I'd do, and figured someone would mention it 😉

Copy link
Contributor

Choose a reason for hiding this comment

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

I've seen this pattern floating around a little also:

fn bar<T, K>(x: T, y: K)
    where
        T: Clone,
        K: Clone + Debug,
{
    ..
}

I find it nicely readable for situations where there are multiple types and many bounds, easy to refactor and plays nice with auto-indent.

Copy link
Contributor

Choose a reason for hiding this comment

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

I also recall seeing this somewhere in the std src:

fn bar<T, K>(x: T, y: K) where
    T: Clone,
    K: Clone + Debug {
    ..
}

Copy link
Contributor

Choose a reason for hiding this comment

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

@mitchmindtree The last one looks great visually, especially if one squints :). But is just begs for { to be on the next line for such multiline case. Also, what about the result:

fn bar<T, K>(x: T, y: K) 
    -> Result<bool, String> 
where
    T: Clone,
    K: Clone + Debug 
{
    ..
}

K: Clone + Debug {

x.clone();
y.clone();
println!("{:?}", y);
}
```

This flexibility can add clarity in complex situations.

`where` is also more powerful than the simpler syntax. For example:
Copy link
Member

Choose a reason for hiding this comment

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

Maybe this could be expanded to be specific about what is happening: where clauses allow bounds where the left-hand side is an arbitrary type, not just a plain type parameter.

Copy link
Member Author

Choose a reason for hiding this comment

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

added


```
trait ConvertTo<Output> {
fn convert(&self) -> Output;
}

impl ConvertTo<i64> for i32 {
fn convert(&self) -> i64 { *self as i32 }
}

// can be called with T == i32
fn normal<T: ConvertTo<i64>>(x: &T) -> i64 {
x.convert()
}

// can be called with T == i64
fn inverse<T>() -> T
// this is using ConvertTo as if it were "ConvertFrom<i32>"
where i32: ConvertTo<T> {
1i32.convert()
}
```

This shows off the additional feature of `where` clauses: they allow bounds
where the left-hand side is an arbitrary type (`i32` in this case), not just a
plain type parameter (like `T`).

## Our `inverse` Example

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