-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
std: replace std::unstable::finally with a RAII finally! macro #11905
Conversation
I feel like the name of this could be improved; I just borrowed it from D without much thought. Maybe something like |
Go uses the |
-1 for My suggestions: |
self.release(); | ||
}) | ||
scope!(self.release()); | ||
self.acquire(); |
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.
Is this release-before-acquire pattern intentional? Would it make sense to have a two-argument macro with a "constructor" expression?
@Jurily: The |
I'd suggest |
cc #9842, which sounds like there may be soundness issues around this strategy. That being said, this is the same strategy as When we initially discussed scope awhile back (and with #9842), it gave us the ability to run functions on exit, failing, or success, and that seems like useful functionality. I suppose the macro could include the literal
It doesn't quite look like the
That also looks a little silly because there's no indication of what |
Maybe just call it |
Reading #6801 (referenced from #9842), it sounds like fixing that issue will make this approach significantly less useful, because any upvalues referenced in the closure will become active borrows for the lifetime of the closure. This may be the same strategy as |
I personally prefer |
The nice thing about the D/Go style is not requiring any additional nesting. |
In some of the replacements here
Are there cases where borrowing forces it to come first? |
The second isn't really even correct:
|
@brson: The first could also fail in |
The second example could be (using lock.lock();
finally!(lock.unlock())
do_something_under_lock(); which isn't entirely horrible (it's certainly better than putting it before the (And I completely agree with using RAII/custom destructors where it happens a lot; certainly mutex lock/unlock code would be cleaner & safer with it.) |
FWIW, I prefer |
Also, I decided to avoid |
Not speaking in terms of functionality but I like |
This adds a `finally!` macro similar to D's scope statement. It runs a user specified piece of code in the destructor of a stack variable to allow guaranteeing that it runs, even on failure.
Updated with it renamed to |
@nikomatsakis says he has a patch for #6801. I'm expecting it to impact |
Sounds good (also, this has been added to the meeting agenda). |
The macro I wrote to do this was called |
To be clear, I prefer finally blocks to the defer pattern. I don't know whether I prefer |
I don't know what the defer pattern is, it just seemed like the obvious On Sat, Feb 1, 2014 at 6:39 PM, Brian Anderson notifications@git.luolix.topwrote:
|
One disadvantage of the (current) implementation of finally!(/* run this */, /* then always run this*/) (where the first part is expanded inline, without requiring a closure.) |
I think there is a definite risk of confusion in calling it |
The examples here do look better as written, and most are easy to understand. It seems like where with try/finally you would write:
Now you would write
This is more concise, and I can see how in these common cases it is also possibly even more clear than try/finally on a top-down reading. |
The changes for #2202 (actually #6801) to be more precise require that if a variable is mutable, it is effectively captured by the closure. This impacts try-finally patterns that update a variable in the try block and then access it in the finally block. For example, code like this would not compile:
I wound up adding a new
Then you can translate the above code into:
You can extend this to multiple variables using a tuple or some such but I never had to. An alternative is to use a I hope to write up the details in a blog post but I thought I'd put up a summary here briefly. There is a bit of wiggle room here -- in an earlier version, I permitted (in some cases) variables to be mutated in one closure but read in another, so long as they were not mutated in both, this was effectively bringing back const (read-only but not immutable) borrows. But I eventually removed it because it wasn't used that frequently. This is something I had hoped to write up and discuss, there are some tradeoffs involved. |
Since we're going for RAII semantics here, why not call it |
We decided in today's meeting to move towards this macro, but call it @huonw, are you ok implementing that change? |
Are you strongly opposed to a built-in |
I think built-in scope guards would make sense, but not another block-based control structure. Rust shouldn't take a step backwards from D and Go by requiring a bunch of nesting for this. Stack-based once functions are going to feel like a missing feature if closures are used for RAII patterns like this. They were left out with the rationale that we weren't going to do this anymore... |
|
C++ doesn't have Rust already has destructors, so the RAII idiom is already part of the language. Stack-based destructors are an idiom every Rust programmer is familiar with. |
I'd expect C++ programmers to be familiar with On the other hand, non-C++ programmers potentially interested in Rust are a lot less likely to be familiar/comfortable with the hoops C++ programmers jump through to emulate Of course maybe I'm completely off base with that, or maybe C++ programmers make up such an overwhelming majority of the Rust demographic that it doesn't matter anyway, but I think it's worth considering just as one aspect among many contributing to this decision. |
That's a mischaracterization of scope guards. They're not an emulation of
D has scope guards and they're almost always regarded as a far better option than
Scope guards avoid nesting, are more familiar to C++ programmers and would only require one keyword if they were included in the language rather than as a macro. What is the advantage of |
@thestinger I don't have anything to add beyond comments already made in this thread. |
I was correct: it seems like the fact that closures are now correct(er) has sunk the neatness of this macro. Possible solutions:
|
@huonw Yeah, it's unfortunate, though I don't anticipate an easy solution. In the meeting we had assumed the macro would only apply to cases where there is no mutable state shared between the two. Possibly the "call out the mutate state" approach could be possibly made to work, though I expect you'd have to write |
I think this is less valuable now, and is better effected by changes like #12235. (Also, the queue is huge and I'm not going to update this right now... closing.) |
Tolerate hidden, binary files in tests/ Avoid scanning temporary files created by editors like this one created by Vim: ---- old_test_headers stdout ---- thread 'old_test_headers' panicked at tests/headers.rs:19:74: tests/ui/.regex.rs.swp: stream did not contain valid UTF-8 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace changelog: none
This adds a
scope!
macro similar to D's scope statement. It runs auser specified piece of code in the destructor of a stack variable to
allow guaranteeing that it runs, even on failure.