-
Notifications
You must be signed in to change notification settings - Fork 13.4k
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
Document where clauses. #22123
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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, | ||
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: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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: | ||
|
There was a problem hiding this comment.
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:
and/or
There was a problem hiding this comment.
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
.There was a problem hiding this comment.
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 😉
There was a problem hiding this comment.
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:
I find it nicely readable for situations where there are multiple types and many bounds, easy to refactor and plays nice with auto-indent.
There was a problem hiding this comment.
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:
There was a problem hiding this comment.
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: