Rollup merge of #28588 - critiqjo:trpl-closure, r=steveklabnik

r? @steveklabnik
This commit is contained in:
Steve Klabnik 2015-09-25 13:33:31 -06:00
commit aed73e0122

View File

@ -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 doesnt kick in
here. What lifetime can we choose? `'static`:
our `factory()` function takes no arguments, so
[elision](lifetimes.html#lifetime-elision) doesnt kick in here. Then what
choices do we have? Try `'static`:
```rust,ignore
fn factory() -> &'static (Fn(i32) -> i32) {
@ -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
@ -441,21 +442,17 @@ error: mismatched types:
```
This error is letting us know that we dont 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 doesnt 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> {
@ -471,7 +468,7 @@ assert_eq!(6, answer);
# }
```
We use a trait object, by `Box`ing up the `Fn`. Theres just one last problem:
Theres just one last problem:
```text
error: closure may outlive the current function, but it borrows `num`,
@ -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> {