Skip to content

trpl: Refactor returning closures section #28588

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
Sep 26, 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
33 changes: 17 additions & 16 deletions src/doc/trpl/closures.md
Original file line number Diff line number Diff line change
Expand Up @@ -411,8 +411,9 @@ fn factory() -> &(Fn(i32) -> i32) {
```

Right. Because we have a reference, we need to give it a lifetime. But
our `factory()` function takes no arguments, so elision doesn’t kick in
here. What lifetime can we choose? `'static`:
our `factory()` function takes no arguments, so
[elision](lifetimes.html#lifetime-elision) doesn’t kick in here. Then what
choices do we have? Try `'static`:

```rust,ignore
fn factory() -> &'static (Fn(i32) -> i32) {
Expand All @@ -432,7 +433,7 @@ But we get another error:
```text
error: mismatched types:
expected `&'static core::ops::Fn(i32) -> i32`,
found `[closure <anon>:7:9: 7:20]`
found `[closure@<anon>:7:9: 7:20]`
(expected &-ptr,
found closure) [E0308]
|x| x + num
Expand All @@ -441,21 +442,17 @@ error: mismatched types:
```

This error is letting us know that we don’t have a `&'static Fn(i32) -> i32`,
we have a `[closure <anon>:7:9: 7:20]`. Wait, what?
we have a `[closure@<anon>:7:9: 7:20]`. Wait, what?

Because each closure generates its own environment `struct` and implementation
of `Fn` and friends, these types are anonymous. They exist just solely for
this closure. So Rust shows them as `closure <anon>`, rather than some
this closure. So Rust shows them as `closure@<anon>`, rather than some
autogenerated name.

But why doesn’t our closure implement `&'static Fn`? Well, as we discussed before,
closures borrow their environment. And in this case, our environment is based
on a stack-allocated `5`, the `num` variable binding. So the borrow has a lifetime
of the stack frame. So if we returned this closure, the function call would be
over, the stack frame would go away, and our closure is capturing an environment
of garbage memory!

So what to do? This _almost_ works:
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:

```rust,ignore
fn factory() -> Box<Fn(i32) -> i32> {
Expand All @@ -471,7 +468,7 @@ assert_eq!(6, answer);
# }
```

We use a trait object, by `Box`ing up the `Fn`. There’s just one last problem:
There’s just one last problem:

```text
error: closure may outlive the current function, but it borrows `num`,
Expand All @@ -480,8 +477,12 @@ Box::new(|x| x + num)
^~~~~~~~~~~
```

We still have a reference to the parent stack frame. With one last fix, we can
make this work:
Well, as we discussed before, closures borrow their environment. And in this
case, our environment is based on a stack-allocated `5`, the `num` variable
binding. So the borrow has a lifetime of the stack frame. So if we returned
this closure, the function call would be over, the stack frame would go away,
and our closure is capturing an environment of garbage memory! With one last
fix, we can make this work:

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