Suggestion: Item blocks

Has there ever been discussion on the topic of item blocks/groups? Specifically extending the grammar for items to:

Item:
  (Module | Struct | Union | ... | ItemBlock )

ItemBlock:
 `{` Item* `}`

The primary use case for this would be allowing an attribute to affect multiple items:

#[cfg(unix)] {
    fn a() { ... }
    fn b() { ... }
    fn c() { ... }
}

which, in my opinion reads a lot nicer than:

#[cfg(unix)]
fn a() { ... }
#[cfg(unix)]
fn b() { ... }
#[cfg(unix)]
fn c() { ... }

or a workaround with modules:

#[cfg(unix)]
mod unix_impl {
    fn a() { ... }
    fn b() { ... }
    fn c() { ... }
}
#[cfg(unix)]
use unix_impl::{a, b, c};
1 Like

Would a, b, and c be visible outside of the block? If so then it seems unintuitive to me, since I'm so used to blocks scoping things.

2 Likes

Hmm it would indeed be consistent with other syntax if they scoped things, acting kind of like anonymous modules; but that sadly isn't very useful.

We've discussed this in the past, and the biggest concern was exactly that problem: the use of braces implies a scope, but isn't one.

Also, the thread subject made me think of this:

11 Likes

By no means similar in purpose, but const { } has the same issue and was deemed acceptable.

1 Like

In my own projects I have been using a macro that does nothing:

macro block($($tt:tt)*) { $($tt)* }

#[cfg(unix)] 
block! {
    fn a() { ... }
    fn b() { ... }
    fn c() { ... }
}

But it always felt like a workaround for something that could be simpler.

6 Likes

I'm pretty sure const {} introduces a new item scope, though. If you want to get a value out, you return it, the same as a non-const {}.

FWIW, I'd expect anonymous modules to be written

mod {
    // ...
}

or

mod _ {
    // ...
}

rather than a bare block.

Personally, I'm not convinced that transparent blocks are really all that useful. If you have one #[cfg(os)], you'll necessarily have at least one more. Also, the likelihood that these require some sort of platform-specific imports to implement their functionality also seems relatively high, at which point you'd like to have them be in a separate namespace to keep those from leaking to the OS-agnostic module.

While I could see the benefit for simple example and/or explanatory code, it seems like most actual use cases would be better served by separate impl files for the different OSes.

And one pattern I've seen to make this easier is using the less-known #[path] attribute for mods to do so for a single mod item:

#[cfg_attr(os(windows), path = "impl_windows")]
#[cfg_attr(os(unix), path = "impl_unix")]
mod impl;

It's up to taste at how many items / how much complexity is enough to warrant pulling out to an auxiliary file like this.

7 Likes