-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
RIP boxed closures #20578
RIP boxed closures #20578
Conversation
my hero. |
I've updated the [breaking-change] message. |
It may be worth noting that you can't actually get the old behavior of boxed closures today (#20575): fn foo(f: &mut FnMut()) {
f();
}
This does work, but requires the feature gate, however: #![feature(unboxed_closures)]
fn foo(mut f: Box<FnMut()>) {
f.call_mut(());
} |
@@ -4231,7 +4231,7 @@ arguments, really powerful things are possible. | |||
|
|||
Let's make a closure: | |||
|
|||
```{rust} | |||
```{rust,ignore} |
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.
Could we un-ignore these for landing?
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.
Yes, ignoring this is bad.
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.
@japaric would you prefer a PR from me to your PR, or do you just want to change the syntax and I can land the language after?
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'd be great if this landed with updated docs, so I'll go with the first option.
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.
let me tackle that for you right now then ❤️
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.
sent in japaric#1
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.
@steveklabnik you need a stage1 compiler (built with this PR) to see the compiler errors in the doctests. Let me update the snippets and then you can update the text around. (That should be faster since I have a stage1 compiler at hand).
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.
Oh, that's... weird. Okay.
@alexcrichton Interestingly that ICE only occurs for trait objects with zero arguments, these two work without the feature gate: fn foo(mut f: Box<FnMut(int)>) {
f(5)
}
fn bar(mut f: &mut FnMut(int)) {
f(5)
} |
@japaric whoa nice! I had no idea! Pretend I was never here... |
👍 👍 |
@alexcrichton I fixed the ICE altogether and sent @japaric the commit. Hopefully it'll show up here at some point. |
swoon |
80febed
to
8c195be
Compare
It's ready! ICEs have been fixed (by @nikomatsakis), unignored and fixed the doctests in the guide and the reference. Last 6 commits are new. |
…trait method with rust-call ABI.
This PR removes boxed closures from the language, the closure type syntax (`let f: |int| -> bool = /* ... */`) has been obsoleted. Move all your uses of closures to the new unboxed closure system (i.e. `Fn*` traits). [breaking-change] patterns - `lef f = || {}` This binding used to type check to a boxed closure. Now that boxed closures are gone, you need to annotate the "kind" of the unboxed closure, i.e. you need pick one of these: `|&:| {}`, `|&mut:| {}` or `|:| {}`. In the (near) future we'll have closure "kind" inference, so the compiler will infer which `Fn*` trait to use based on how the closure is used. Once this inference machinery is in place, we'll be able to remove the kind annotation from most closures. - `type Alias<'a> = |int|:'a -> bool` Use a trait object: `type Alias<'a> = Box<FnMut(int) -> bool + 'a>`. Use the `Fn*` trait that makes sense for your use case. - `fn foo(&self, f: |uint| -> bool)` In this case you can use either a trait object or an unboxed closure: ``` rust fn foo(&self, f: F) where F: FnMut(uint) -> bool; // or fn foo(&self, f: Box<FnMut(uint) -> bool>); ``` - `struct Struct<'a> { f: |uint|:'a -> bool }` Again, you can use either a trait object or an unboxed closure: ``` rust struct Struct<F> where F: FnMut(uint) -> bool { f: F } // or struct Struct<'a> { f: Box<FnMut(uint) -> bool + 'a> } ``` - Using `|x, y| f(x, y)` for closure "borrows" This comes up in recursive functions, consider the following (contrived) example: ``` rust fn foo(x: uint, f: |uint| -> bool) -> bool { //foo(x / 2, f) && f(x) // can't use this because `f` gets moved away in the `foo` call foo(x / 2, |x| f(x)) && f(x) // instead "borrow" `f` in the `foo` call } ``` If you attempt to do the same with unboxed closures you'll hit ""error: reached the recursion limit during monomorphization" (see #19596): ``` rust fn foo<F>(x: uint, mut f: F) -> bool where F: FnMut(uint) -> bool { foo(x / 2, |x| f(x)) && f(x) //~^ error: reached the recursion limit during monomorphization } ``` Instead you *should* be able to write this: ``` rust fn foo<F>(x: uint, mut f: F) -> bool where F: FnMut(uint) -> bool { foo(x / 2, &mut f) && f(x) //~^ error: the trait `FnMut` is not implemented for the type `&mut F` } ``` But as you see above `&mut F` doesn't implement the `FnMut` trait. `&mut F` *should* implement the `FnMut` and the above code *should* work, but due to a bug (see #18835) it doesn't (for now). You can work around the issue by rewriting the function to take `&mut F` instead of `F`: ``` rust fn foo<F>(x: uint, f: &mut F) -> bool where F: FnMut(uint) -> bool { foo(x / 2, f) && (*f)(x) } ``` This finally works! However writing `foo(0, &mut |x| x == 0)` is unergonomic. So you can use a private helper function to avoid this: ``` rust // public API function pub fn foo<F>(x: uint, mut f: F) -> bool where F: FnMut(uint) -> bool { foo_(x, &mut f) } // private helper function fn foo_<F>(x: uint, f: &mut F) -> bool where F: FnMut(uint) -> bool { foo_(x / 2, f) && (*f)(x) } ``` Closes #14798 --- There is more cleanup to do: like renaming functions/types from `unboxed_closure` to just `closure`, removing more dead code, simplify functions which now have unused arguments, update the documentation, etc. But that can be done in another PR. r? @nikomatsakis @aturon (You probably want to focus on the deleted/modified tests.) cc @eddyb
Note the example in the first PR comment is wrong: fn foo(&self, f: F) where F: FnMut(uint) -> bool; You'll get the error fn foo<F>(&self, f: F) where F: FnMut(uint) -> bool; |
The book for moving closure is still using boxed closures as an example: http://doc.rust-lang.org/book/closures.html |
They should all be unboxed. Which are boxed? |
(e.g., move || x * x). I'm pretty sure that syntax is invalid now with 1.0? |
|
The |
This test was ignored long ago in rust-lang#20578 when the syntax for closures was changed. The current status is that a closure with an explicit `!` return type will trigger the `unreachable_code` lint which appears to be the original intent of the test (rust-lang#16836). A closure without a return type won't trigger the lint since the `!` type isn't inferred (AFAIK). This restores the test to its original form.
This PR removes boxed closures from the language, the closure type syntax (
let f: |int| -> bool = /* ... */
) has been obsoleted. Move all your uses of closures to the new unboxed closure system (i.e.Fn*
traits).[breaking-change] patterns
lef f = || {}
This binding used to type check to a boxed closure. Now that boxed closures are gone, you need to annotate the "kind" of the unboxed closure, i.e. you need pick one of these:
|&:| {}
,|&mut:| {}
or|:| {}
.In the (near) future we'll have closure "kind" inference, so the compiler will infer which
Fn*
trait to use based on how the closure is used. Once this inference machinery is in place, we'll be able to remove the kind annotation from most closures.type Alias<'a> = |int|:'a -> bool
Use a trait object:
type Alias<'a> = Box<FnMut(int) -> bool + 'a>
. Use theFn*
trait that makes sense for your use case.fn foo(&self, f: |uint| -> bool)
In this case you can use either a trait object or an unboxed closure:
struct Struct<'a> { f: |uint|:'a -> bool }
Again, you can use either a trait object or an unboxed closure:
|x, y| f(x, y)
for closure "borrows"This comes up in recursive functions, consider the following (contrived) example:
If you attempt to do the same with unboxed closures you'll hit ""error: reached the recursion limit during monomorphization" (see #19596):
Instead you should be able to write this:
But as you see above
&mut F
doesn't implement theFnMut
trait.&mut F
should implement theFnMut
and the above code should work, but due to a bug (see #18835) it doesn't (for now).You can work around the issue by rewriting the function to take
&mut F
instead ofF
:This finally works! However writing
foo(0, &mut |x| x == 0)
is unergonomic. So you can use a private helper function to avoid this:Closes #14798
There is more cleanup to do: like renaming functions/types from
unboxed_closure
to justclosure
, removing more dead code, simplify functions which now have unused arguments, update the documentation, etc. But that can be done in another PR.r? @nikomatsakis @aturon (You probably want to focus on the deleted/modified tests.)
cc @eddyb