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

Tracking issue for FnBox() #28796

Closed
abonander opened this issue Oct 1, 2015 · 84 comments · Fixed by #62043
Closed

Tracking issue for FnBox() #28796

abonander opened this issue Oct 1, 2015 · 84 comments · Fixed by #62043
Labels
B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC disposition-close This PR / issue is in PFCP or FCP with a disposition to close it. finished-final-comment-period The final comment period is finished for this PR / Issue. T-lang Relevant to the language team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.

Comments

@abonander
Copy link
Contributor

This is a tracking issue for the fn_box feature and FnBox trait, which allows a Box<FnOnce(...) -> _> closure to be invoked via the "rust-call" sugar.

I'm not sure what the primary concerns are. All I know is it'd be really, really nice if we could invoke a Box<FnOnce()> somehow in safe Rust. This may or may not be the best approach.

@eddyb feel free to CC anyone who might want to comment or edit this issue to add any pertinent information.

@alexcrichton alexcrichton added T-libs-api Relevant to the library API team, which will review and decide on the PR/issue. B-unstable Blocker: Implemented in the nightly compiler and unstable. labels Oct 1, 2015
@eefriedman
Copy link
Contributor

The reason we don't want to stabilize FnBox is because we would prefer to make Box<FnOnce()> just work.

Making Box<FnOnce()> work isn't exactly controversial (there's only one obvious way to handle the construct), but it's sort of tied up with rust-lang/rfcs#997, and more generally the issue that we want to remove the special cases for Box from the compiler.

@charlesetc
Copy link

So in the stable version of rust it's impossible to call Box<FnOnce()> and it's impossible to return a closure that's not in a box, judging from the book, because it doesn't have a known size. So how does one return a closure and call it?

@abonander
Copy link
Contributor Author

@charlesetc Box<Fn()> and Box<FnMut()> can both be called without a problem, because they don't require destructuring the box they're in; they only need &self or &mut self, respectively, which totally works with the current deref coercions. Which of the closure traits, Fn(), FnMut(), or FnOnce(), is implemented for a specific closure is determined by inference.

Box<FnOnce()> is a challenge because it takes self by-value, which means it needs to be moved out of the Box and onto the stack to be executed, but this doesn't work in current Rust because there's no stable way to move out of (destructure) a Box. The DerefMove trait proposed in the RFC @eefriedman linked would make this work, but the design of it is still being worked on.

The key here is that Box<FnOnce()> does have a known size, but it's hidden behind the Box pointer so that anything implementing FnOnce, regardless of its size, can fit in the same spot. The v-table in the trait object directs the processor to the concrete implementation of the trait (in this case, the closure body) for that specific object. All the details about the object, its size, fields, etc., are coded into that implementation.

@charlesetc
Copy link

Oh that makes more sense, thanks!

@kosta
Copy link

kosta commented Oct 2, 2015

I don't want to be pushy, but I think this is really a major missing piece in Rusts "Closures" story - and not really mentioned on in the rust book.

I understand that you want to remove special-cases for Box in the compiler and I agree that that's a worthy goal. But when will that land?

If we can have Box<FnOnce> right now for the price of "a bit more Box magic" until the no-special-case-for-Box story is complete, then is that maybe the right price to pay?
(I couldn't say, I would just profit from it as a Rust user)

@alexcrichton
Copy link
Member

Nominating for discussion in 1.6

@nikomatsakis
Copy link
Contributor

cc me

@alexcrichton
Copy link
Member

🔔 This issue is now entering its cycle-long final comment period 🔔

The libs team discussed this and we have two possible routes for this right now:

  1. The in-tree copy could be stabilized with the intention of immediately deprecating once by-value DST works. The in-tree copy has the benefit of "being variadic" and having the paren sugar in bounds.
  2. The in-tree copy could be deprecated and a crates.io crate could provide a suite of traits like FnBox1, FnBox2, etc. This has the benefit of not polluting the standard library but is less ergonomic to use.

We're a little undecided on which route to take, so some comments would be welcome!

@alexcrichton alexcrichton added final-comment-period In the final comment period and will be merged soon unless new substantive objections are raised. and removed I-nominated labels Nov 5, 2015
@abonander
Copy link
Contributor Author

I'd vote for the former. We have enough micro-crates in the ecosystem and this is a common enough pattern that it's worth supporting in the stdlib.

Edit: I want to add that I do have a use for this. I need it for one-off callbacks in wingui. Right now I'm having to wrap FnOnce callbacks in an Option and move it into an FnMut closure so I can call it from a box. It's not very ergonomic.

I don't want to have to add another crate to my build because that seems like a very heavyweight solution just to get a trait or two.

@sfackler
Copy link
Member

sfackler commented Nov 5, 2015

@cybergeek94 you can do a bit better than that with some boilerplate: https://github.com/sfackler/r2d2/blob/master/src/thunk.rs but it's a bit of a pain.

@abonander
Copy link
Contributor Author

It's clunky any way you spin it.

@bstrie
Copy link
Contributor

bstrie commented Nov 5, 2015

Given that we all understand that FnBox is a will-inevitably-be-deprecated-hopefully-sooner-rather-than-later hack (albeit a useful one), I would like commitment from the Rust devs on some kind of timeframe for fixing the underlying issues before letting this be stabilized. Being able to say "this will be deprecated within the next year" is much more reassuring than "this will be deprecated, uhm, iunno, sometime".

@sfackler
Copy link
Member

sfackler commented Nov 5, 2015

What's wrong with "this will be deprecated, uhm, iunno, sometime"? If we don't have a timeline for by-value trait objects, why would we not want to have this bridge?

@dlight
Copy link

dlight commented Nov 8, 2015

I don't want to have to add another crate to my build because that seems like a very heavyweight solution just to get a trait or two.

Well, the "it lives on crates.io until it's stabilized" pattern has been applied to other cases, it's a stopgap solution that's cleaner than introducing something with the intent of deprecating it.

@sinclairzx81
Copy link

Hi,

With the current implementation of FnBox, I note that drop() is not implicitly called for captured members at the end of a boxed closure, which may (or may not) be a unintended source of leaking memory if relying solely on Rust's move semantics. I have produced a gist to demonstrate what i believe to be going on.

https://play.rust-lang.org/?gist=aa96e9e0fe78a09af2eb&version=nightly
https://gist.github.com/aa96e9e0fe78a09af2eb

I am not sure of the future of FnBox, (certainly would love to see a full blown Box<FnOnce> implementation if under review for a 1.6 release), but in the interim, I wonder if its possible to address this specifically to bring things inline.

Thanks all

@eefriedman
Copy link
Contributor

@sinclairzx81 Filed #29946.

@sinclairzx81
Copy link

@eefriedman Brilliant, thank you.

@nikomatsakis
Copy link
Contributor

I think I would stabilize this, personally. My reasoning is:

  1. Anything we can do to make things more ergonomic here is good, until such time as Box<FnOnce()> works properly.
  2. The original idea of FnBox was that it would serve as a place-holder for where you would use Box<FnOnce> such that, in the glorious future, you can basically s/FnBox/FnOnce/ and Everything Just Works. I think this is mostly true now. This will be rather untrue if we move things to a crates.io crate. Obviously I don't really expect anyone to run sed, but I think having FnBox work as much like FnOnce as we can makes it easier to explain to newcomers: "ah, Box<FnOnce> doesn't quite work yet, but you can do Box<FnBox> for now, and it mostly works the same except that it requires a box".

Basically, our story here is not great, but I don't see any reason to make it worse, just to avoid a deprecated item in the standard library.

@alexcrichton
Copy link
Member

The libs team discussed this during triage yesterday and the decision was to stabilize. @bluss brought up a good point, however, that Box<FnMut<A>> doesn't implement FnMut<A> for coherence reasons related to FnBox which may be good to sort out, however. Concretely, an impl like

impl<F: ?Sized, A> FnMut<A> for Box<F> where F: FnMut<A> {
    ...
}

does not work. An impl of FnMut requires an impl of Fn which in turn requires an impl of FnOnce:

impl<F: ?Sized, A> FnOnce<A> for Box<F> where F: FnOnce<A> {
    ...
}

Unfortunately this impl can't be written for two reasons:

  1. Unsized types by value don't work, so we can't use .call_once or * to move out of the box if F: ?Sized.
  2. This impl is a coherence violation with FnOnce<A> for Box<FnBox<A>>.

@eddyb
Copy link
Member

eddyb commented Dec 3, 2015

@nikomatsakis What's blocking Box<FnOnce()> from "just working"?
FnBox seems like a lot of work for a band-aid.

@alexcrichton
Copy link
Member

Ok, so I'm wary to stabilize FnBox if it prevents us from later having the impls for Box<FnMut()> to also implement FnMut(), and to drill down a bit I've come up with this small sample:

#![crate_type = "lib"]
#![feature(fundamental)]

#[fundamental]
trait FnBox<A> {}
#[fundamental]
trait FnOnce<A> {}

struct Box<T: ?Sized>(T);

impl<A> FnOnce<A> for Box<FnBox<A>> {}

impl<F: FnOnce<A> + ?Sized, A> FnOnce<A> for Box<F> {}

This represents basically the traits that are in play here, but doesn't worry about the unsized types by value problem. Currently this code yields a coherence violation:

<anon>:11:1: 11:39 error: conflicting implementations for trait `FnOnce` [E0119]
<anon>:11 impl<A> FnOnce<A> for Box<FnBox<A>> {}
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<anon>:11:1: 11:39 help: see the detailed explanation for E0119
<anon>:13:1: 13:55 note: note conflicting implementation here
<anon>:13 impl<F: FnOnce<A> + ?Sized, A> FnOnce<A> for Box<F> {}
          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error

Yet I would think that because of the fundamental attribute the compiler should be able to know that the type FnBox<A> does not implement FnOnce<A> so the two impls can never overlap. @nikomatsakis mentioned that #19032 may come into play here.

For now I'm not going to stabilize FnBox while we sort this out, but it's certainly a candidate for backporting quickly!

@sfackler
Copy link
Member

sfackler commented Dec 3, 2015

@eddyb DST by value

@glaebhoerl
Copy link
Contributor

"DST by value" or its explicit / first-class formulation, &move.

@Ericson2314
Copy link
Contributor

@glaebhoerl they are different:

  • fn foo(&move DST) -> SST doesn't copy bits into its stack frame
  • fn foo(DST) -> SST does copy bits.
  • fn foo(SST) -> &move DST better be refining a move reference as it's stack frame doesn't live long enough.
  • fn foo(SST) -> DST copies bits into receiver.

Now for making a realistic API &move and &fillMeIn may be used under the hood, just as they are for certain return values today, but that is an implementation detail. Out pointers are not very ergonomic.

@eddyb
Copy link
Member

eddyb commented Mar 21, 2019

@gnzlbg Where is this requirement for libtest tracked, or where in the source is it needed? I'm not sure but I would suspect you might want your own trait anyway, that is implemented for closures but also allows manual implementations.

Alternatively, you could potentially have a wrapper that exposes only a constructor with an F: FnOnce(...) -> ... bound, and internally uses a private trait that works like FnBox.

@crlf0710
Copy link
Member

impl FnOnce for Box<FnOnce()> will be stable in 1.35.

@SimonSapin
Copy link
Contributor

PR to add a deprecation warning: #61113.

We can remove FnBox entirely and close this issue a couple release cycles after this lands.

SimonSapin added a commit to SimonSapin/rust that referenced this issue May 24, 2019
Centril added a commit to Centril/rust that referenced this issue May 24, 2019
Deprecate `FnBox`. `Box<dyn FnOnce()>` can be called directly, since 1.35

FCP completion: rust-lang#28796 (comment)
Centril added a commit to Centril/rust that referenced this issue May 24, 2019
Deprecate `FnBox`. `Box<dyn FnOnce()>` can be called directly, since 1.35

FCP completion: rust-lang#28796 (comment)
Centril added a commit to Centril/rust that referenced this issue May 24, 2019
Deprecate `FnBox`. `Box<dyn FnOnce()>` can be called directly, since 1.35

FCP completion: rust-lang#28796 (comment)
Centril added a commit to Centril/rust that referenced this issue May 25, 2019
Deprecate `FnBox`. `Box<dyn FnOnce()>` can be called directly, since 1.35

FCP completion: rust-lang#28796 (comment)
rubdos added a commit to rubdos/bulletproofs that referenced this issue May 28, 2019
It allows for the challenge phase to accept closures that take some more
context, e.g. moving non-Clone values in the challenge closure.

This works in stable since Rust 1.35; cfr.
rust-lang/rust#28796.

This closes issue dalek-cryptography#244.
@Centril Centril mentioned this issue Jun 22, 2019
Centril added a commit to Centril/rust that referenced this issue Jun 26, 2019
Remove `FnBox`

Remove `FnBox` since we now have `Box<dyn FnOnce>`.

Closes rust-lang#28796.

r? @cramertj
Centril added a commit to Centril/rust that referenced this issue Jun 26, 2019
Remove `FnBox`

Remove `FnBox` since we now have `Box<dyn FnOnce>`.

Closes rust-lang#28796.

r? @cramertj
Centril added a commit to Centril/rust that referenced this issue Jun 27, 2019
Remove `FnBox`

Remove `FnBox` since we now have `Box<dyn FnOnce>`.

Closes rust-lang#28796.

r? @cramertj
Centril added a commit to Centril/rust that referenced this issue Jun 27, 2019
Remove `FnBox`

Remove `FnBox` since we now have `Box<dyn FnOnce>`.

Closes rust-lang#28796.

r? @cramertj
Centril added a commit to Centril/rust that referenced this issue Jun 27, 2019
Remove `FnBox`

Remove `FnBox` since we now have `Box<dyn FnOnce>`.

Closes rust-lang#28796.

r? @cramertj
Centril added a commit to Centril/rust that referenced this issue Jun 27, 2019
Remove `FnBox`

Remove `FnBox` since we now have `Box<dyn FnOnce>`.

Closes rust-lang#28796.

r? @cramertj
nastevens added a commit to saltlick-crypto/saltlick-rs that referenced this issue Feb 27, 2020
Since the MSRV of the crate has been updated, we can remove the
workaround for rust-lang/rust#28796 and just
use a Box<FnOnce> directly.

Signed-off-by: Nick Stevens <nick@bitcurry.com>
nastevens added a commit to saltlick-crypto/saltlick-rs that referenced this issue Mar 6, 2020
Since the MSRV of the crate has been updated, we can remove the
workaround for rust-lang/rust#28796 and just
use a Box<FnOnce> directly.

Signed-off-by: Nick Stevens <nick@bitcurry.com>
rubdos added a commit to rubdos/bulletproofs that referenced this issue May 14, 2020
It allows for the challenge phase to accept closures that take some more
context, e.g. moving non-Clone values in the challenge closure.

This works in stable since Rust 1.35; cfr.
rust-lang/rust#28796.

This closes issue dalek-cryptography#244.
rubdos added a commit to rubdos/bulletproofs that referenced this issue May 20, 2020
It allows for the challenge phase to accept closures that take some more
context, e.g. moving non-Clone values in the challenge closure.

This works in stable since Rust 1.35; cfr.
rust-lang/rust#28796.

This closes issue dalek-cryptography#244.
rubdos added a commit to rubdos/bulletproofs that referenced this issue May 20, 2020
It allows for the challenge phase to accept closures that take some more
context, e.g. moving non-Clone values in the challenge closure.

This works in stable since Rust 1.35; cfr.
rust-lang/rust#28796.

This closes issue dalek-cryptography#244.
oleganza pushed a commit to dalek-cryptography/bulletproofs that referenced this issue May 26, 2020
It allows for the challenge phase to accept closures that take some more
context, e.g. moving non-Clone values in the challenge closure.

This works in stable since Rust 1.35; cfr.
rust-lang/rust#28796.

This closes issue #244.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
B-unstable Blocker: Implemented in the nightly compiler and unstable. C-tracking-issue Category: An issue tracking the progress of sth. like the implementation of an RFC disposition-close This PR / issue is in PFCP or FCP with a disposition to close it. finished-final-comment-period The final comment period is finished for this PR / Issue. T-lang Relevant to the language team, which will review and decide on the PR/issue. T-libs-api Relevant to the library API team, which will review and decide on the PR/issue.
Projects
None yet
Development

Successfully merging a pull request may close this issue.