From time to time, people need to introduce an inner scope within a function like:
fn foo() {
if condition() {
do_something();
}
{
let bar = make_a_value_with_drop_effect();
}
if condition() {
do_something();
}
}
However the 'orphaned brace pair' looks rather awkward here. I suggest we introduce an optional keyword here to 'hold' this inner scope. I suggest 'scope' as an initial option, now the example above looks like:
fn foo() {
if condition() {
do_something();
}
scope {
let bar = make_a_value_with_drop_effect();
}
if condition() {
do_something();
}
}
After a second thought, i think word like 'always' looks nice here, too.
I've never felt the need for this myself, however... A more general approach might be to allow prefixing arbitrary blocks with lifetimes as in:
fn foo() {
if condition() {
do_something();
}
'some_descriptive_name: {
let bar = make_a_value_with_drop_effect();
}
if condition() {
do_something();
}
}
This generalization is provided by RFC https://github.com/rust-lang/rfcs/pull/2046.
@Centril Hmm, not a good idea. In principle the effect is the same , however 'unused' lifetime labels are something that should be lint against, to be consistent with other rules in Rust.
@crlf0710 You could just prefix the lifetime name with _ as in '_some_descriptive_name as done with let bindings to silence the warning.
(I don't have any feelings about the original proposal, but) I don't think the alternative really works. If I see a weird unused lifetime annotation, my first thought is "what is this used for? why is it here?", which doesn't work very well to fulfill the original goal of making the purpose of an anonymous block more obvious.
@crlf0710 There's still good old let binding. Feels too much like a work-around, but this is what I would do.
fn main() {
let _scope = {
let bar = make_a_value_with_drop_effect();
};
// or, why not...
/* scope */ {
let bar = make_a_value_with_drop_effect();
}
// ... ?
}
In my (average) experience, 95% of the times I introduce new scopes is for acquiring _something_ and then binding it to a variable (or the code ends up in a function or closure).
Never been tickled by empty scopes, but if I was, I guess I would just prepend a comment and call it a day.
EDIT: But yeah, that kind of let binding stops looking good as soon as there are many of them in a row - I have a hard time picturing situations which would call for this though.
@yoanlcq I thought the prepended comment might be messed up by rustfmt before. Just now I gave it a try and gladly see that it is kept in the original position (i'm not sure rustfmt will kept this behavior forever though)...
Well such an orphaned scope isn't that rare. Many times i just keep BorrowMut, Cow or MutexGuard for a while, then drop it so i can use the containing value again.
It feels awfully heavy to introduce a keyword for this, since it literally does nothing. Especially since it would lead to questions like "what's the difference between scope {} and just {}?"
I think the answer here is just to accept that they exist, and maybe suggest newlines before them, perhaps with a comment about why the block exists.
fn foo() {
if condition() {
do_something();
}
// Cleanup code will deadlock unless the critsec is released early
{
let bar = foo();
foo(*bar);
}
if condition() {
do_something();
}
}
Lastly, let's not forget about the drop() function, which is definitely an option to avoid introducing new scopes.
If this discussion was to go any further, I think we would benefit from knowing the heart of the problem we actually want to solve. Is it a readability issue ? An aesthetic issue ? Or both ?
Here are some more thoughts, but most of what needed to be said, has been said.
In C++ this kind of orphan scope is common, but people just deal with it (at the same time, they can't complain, they're not part of the committee).
The Qt framework does "introduce new keywords" via preprocessor defines. One example I know is emit (https://stackoverflow.com/q/10160476/7972165).
There's no preprocessor in Rust but I guess build scripts could do something similar (even so, the code will look surprising to contributors).
Also I could imagine a "pass-through" scope!{} macro.
And... Yeah this is all probably overkill. I would just accept the current situation as it is. Keywords are a big deal.
Thanks for everyone's advice. I'm going to close this. Hopefully NLL will help reducing such orphaned scopes, too!
(Now, from a revisionist history perspective, it would have been nice if a keyword _was_ needed to introduce a scope, as that would allow us to use unprefixed braces for struct literals or something.)
Most helpful comment
Thanks for everyone's advice. I'm going to close this. Hopefully NLL will help reducing such orphaned scopes, too!