Summary

Allow block expressions in statics, as long as they only contain items and a trailing const expression.

Example:

static FOO: uint = { 100 };
static BAR: fn() -> int = {
    fn hidden() -> int {
        42
    }
    hidden
};

Motivation

This change allows defining items as part of a const expression, and evaluating to a value using them. This is mainly useful for macros, as it allows hiding complex machinery behind something that expands to a value, but also enables using unsafe {} blocks in a static initializer.

Real life examples include the regex! macro, which currently expands to a block containing a function definition and a value, and would be usable in a static with this.

Another example would be to expose a static reference to a fixed memory address by dereferencing a raw pointer in a const expr, which is useful in embedded and kernel, but requires a unsafe block to do.

The outcome of this is that one additional expression type becomes valid as a const expression, with semantics that are a strict subset of its equivalent in a function.

Drawbacks

Block expressions in a function are usually just used to run arbitrary code before evaluating to a value. Allowing them in statics without allowing code execution might be confusing.

Detailed design

A branch implementing this feature can be found at https://github.com/Kimundi/rust/tree/const_block.

It mainly involves the following changes:

  • const check now allows block expressions in statics:
    • All statements that are not item declarations lead to an compile error.
  • trans and const eval are made aware of block expressions:
    • A trailing expression gets evaluated as a constant.
    • A missing trailing expressions is treated as a unit value.
  • trans is made to recurse into static expressions to generate possible items.

Things like privacy/reachability of definitions inside a static block are already handled more generally at other places, as the situation is very similar to a regular function.

The branch also includes tests that show how this feature works in practice.

Alternatives

Because this feature is a straight forward extension of the valid const expressions, it already causes a very minimal impact on the language, with most alternative ways of enabling the same benefits being more complex.

For example, a expression AST node that can include items but is only usable from procedural macros could be added.

Not having this feature would not prevent anything interesting from getting implemented, but it would lead to less nice looking solutions.

For example, a comparison between static-supporting regex! with and without this feature:

// With this feature, you can just initialize a static:
static R: Regex = regex!("[0-9]");

// Without it, the static needs to be generated by the
// macro itself, alongside all generated items:
regex! {
    static R = "[0-9]";
}

Unresolved questions

None so far.