Skip to content
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

Remove old (boxed) closures from the language #14798

Closed
pcwalton opened this issue Jun 10, 2014 · 5 comments · Fixed by #20578
Closed

Remove old (boxed) closures from the language #14798

pcwalton opened this issue Jun 10, 2014 · 5 comments · Fixed by #20578
Labels
A-closures Area: Closures (`|…| { … }`) A-typesystem Area: The type system
Milestone

Comments

@pcwalton
Copy link
Contributor

We will want to remove (old) boxed closures from the language for 1.0, I believe. At the very least we should feature gate them.

Nominating for 1.0, P-backcompat-lang.

@pnkfelix pnkfelix changed the title Remove boxed closures from the language Remove (old) boxed closures from the language Jun 12, 2014
@emberian emberian changed the title Remove (old) boxed closures from the language Remove old (boxed) closures from the language Jun 12, 2014
@pnkfelix
Copy link
Member

Assigning P-backcompat-lang, 1.0.

@Rdbaker
Copy link

Rdbaker commented Jun 30, 2014

How would one go about this?

@huonw
Copy link
Member

huonw commented Jun 30, 2014

@Rdbaker the new closures don't quite exist yet (#14539), so the best thing for now is waiting. :)

@aturon aturon mentioned this issue Oct 16, 2014
47 tasks
@gamazeps
Copy link
Contributor

gamazeps commented Nov 3, 2014

@huonw and now ? :p

@huonw huonw added the A-closures Area: Closures (`|…| { … }`) label Nov 3, 2014
@huonw
Copy link
Member

huonw commented Nov 3, 2014

Not really ready. See #18101. In particular #17661.

bors added a commit that referenced this issue Jan 5, 2015
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
bors pushed a commit to rust-lang-ci/rust that referenced this issue Aug 21, 2023
bors added a commit to rust-lang-ci/rust that referenced this issue Aug 21, 2023
add check.ignore to list cargo check diagnostics to ignore (dead_code, unused_imports, ...)

fixes rust-lang#14798
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: Closures (`|…| { … }`) A-typesystem Area: The type system
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants