Skip to content

trpl: Clarify closure terminology #28856

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 3 commits into from
Oct 8, 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
45 changes: 24 additions & 21 deletions src/doc/trpl/closures.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
% Closures

Rust not only has named functions, but anonymous functions as well. Anonymous
functions that have an associated environment are called ‘closures’, because they
close over an environment. Rust has a really great implementation of them, as
we’ll see.
Sometimes it is useful to wrap up a function and _free variables_ for better
clarity and reuse. The free variables that can be used come from the
enclosing scope and are ‘closed over’ when used in the function. From this, we
get the name ‘closures’ and Rust provides a really great implementation of
them, as we’ll see.

# Syntax

Expand Down Expand Up @@ -34,7 +35,7 @@ assert_eq!(4, plus_two(2));
```

You’ll notice a few things about closures that are a bit different from regular
functions defined with `fn`. The first is that we did not need to
named functions defined with `fn`. The first is that we did not need to
annotate the types of arguments the closure takes or the values it returns. We
can:

Expand All @@ -44,14 +45,15 @@ let plus_one = |x: i32| -> i32 { x + 1 };
assert_eq!(2, plus_one(1));
```

But we don’t have to. Why is this? Basically, it was chosen for ergonomic reasons.
While specifying the full type for named functions is helpful with things like
documentation and type inference, the types of closures are rarely documented
since they’re anonymous, and they don’t cause the kinds of error-at-a-distance
problems that inferring named function types can.
But we don’t have to. Why is this? Basically, it was chosen for ergonomic
reasons. While specifying the full type for named functions is helpful with
things like documentation and type inference, the full type signatures of
closures are rarely documented since they’re anonymous, and they don’t cause
the kinds of error-at-a-distance problems that inferring named function types
can.

The second is that the syntax is similar, but a bit different. I’ve added spaces
here for easier comparison:
The second is that the syntax is similar, but a bit different. I’ve added
spaces here for easier comparison:

```rust
fn plus_one_v1 (x: i32) -> i32 { x + 1 }
Expand All @@ -63,8 +65,8 @@ Small differences, but they’re similar.

# Closures and their environment

Closures are called such because they ‘close over their environment’. It
looks like this:
The environment for a closure can include bindings from its enclosing scope in
addition to parameters and local bindings. It looks like this:

```rust
let num = 5;
Expand Down Expand Up @@ -197,9 +199,10 @@ frame. Without `move`, a closure may be tied to the stack frame that created
it, while a `move` closure is self-contained. This means that you cannot
generally return a non-`move` closure from a function, for example.

But before we talk about taking and returning closures, we should talk some more
about the way that closures are implemented. As a systems language, Rust gives
you tons of control over what your code does, and closures are no different.
But before we talk about taking and returning closures, we should talk some
more about the way that closures are implemented. As a systems language, Rust
gives you tons of control over what your code does, and closures are no
different.

# Closure implementation

Expand Down Expand Up @@ -288,9 +291,9 @@ isn’t interesting. The next part is:
# some_closure(1) }
```

Because `Fn` is a trait, we can bound our generic with it. In this case, our closure
takes a `i32` as an argument and returns an `i32`, and so the generic bound we use
is `Fn(i32) -> i32`.
Because `Fn` is a trait, we can bound our generic with it. In this case, our
closure takes a `i32` as an argument and returns an `i32`, and so the generic
bound we use is `Fn(i32) -> i32`.

There’s one other key point here: because we’re bounding a generic with a
trait, this will get monomorphized, and therefore, we’ll be doing static
Expand Down Expand Up @@ -452,7 +455,7 @@ autogenerated name.
The error also points out that the return type is expected to be a reference,
but what we are trying to return is not. Further, we cannot directly assign a
`'static` lifetime to an object. So we'll take a different approach and return
a "trait object" by `Box`ing up the `Fn`. This _almost_ works:
a trait object by `Box`ing up the `Fn`. This _almost_ works:

```rust,ignore
fn factory() -> Box<Fn(i32) -> i32> {
Expand Down