Quick Thought: const{} blocks

EDIT many years later: This got added and was stabilized :tada:
https://blog.rust-lang.org/2024/06/13/Rust-1.79.0.html#inline-const-expressions


Looking at async fn+async{} and unsafe fn+unsafe{} made me think, what about const{}?

As an example, (function-level) static_assert!(N == M) could just be const { assert_eq!(N, M) } (Ref Idea: Static assertions & RFC #2345 Allow panicking in constants)

I could also imagine something like match x { 0..N => ..., N..const{2*N} => ..., _ => ... }.

You could think of this as const { A } => (const || { A })(), like with async -- not that const closures exist today.

18 Likes

What are the proposed semantics of such a block construct?

  1. Can you mention any variables from outside the construct? (for example const generic parameters to a function?)
  2. Should use of const { .. } be linted / allowed inside const fn and indeed nested uses of const { .. }?
  3. Are the contents of const { .. } always evaluated at compile time?
2 Likes
  1. Ideally yes, like with async{} and unsafe{}, once const generics exist. But as a first step it could follow const item rules, and thus would only be able to reference other const items.
  2. Yes, same as with nested unsafe{}
  3. That’s what I was picturing, yes. Like expressions used in the length of an array.
5 Likes

It never occurred to me to try this -- but TIL.

I think this idea is worthwhile and your answers all seem reasonable.

6 Likes

I like this too :slight_smile:

I think this might involve the same “errors at monomorphization time” problem, though.

There would also definitely need to be a lint for redundant usage. For example

const FOO: usize = const { 4 };

I’m guessing that any errors that occur evaluating the code in a const block become compiler time errors?

1 Like

I might be crazy, but my first thought on seeing this topic title was “oh neat, we’ll get lazy_static built into the language”. I don’t know how related that would be to your idea, just food for thought.

(My main use case is just const HashMaps or the like.)

That would then need to be a const pattern (?). Not sure this particular addition would be worth the complexity, one could simply calculate a separate constant and use this instead, which may be more desirable for documentation purposes, anyway.

const MAX = 2 * N;
match x { 0..N => ..., N..MAX => ..., _ => ... }

:+1: to the rest, though! :slight_smile:

One neat use case for this would be implementing a generic parse! macro. We already have FromStr for types that can be parsed from strings, but it’s impossible to use at compile-time leading to things like this crate.

With const blocks this macro could simply expand to

const { FromStr::from_str("some string").unwrap() }

Then I could write code like:

let x: u32 = parse!("123");
let y: String = parse!("hello");
let x: PublicKey = parse!("tgta61gja8w7099b17bc33sp49n1zawr037p1n4n7n19qkmgtf9g");
// etc.

And have the parsing (and any errors) happen at compile time.

1 Like

An interesting example just came up on discord:

match ch {
    'z' as i32 => { ... }

Would certainly be nice to not need manual consts for each keycode to be able to match them.

(Would also remove the ambiguity between a binding and matching a const…)

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.