Inline const: use of generic parameter from outer item

Playground

struct Foo<const N: usize>;

impl<const N: usize> Foo<N> {
    fn bar() {
        const NUM: usize = const { N * 2 };
        println!("{}", const { NUM + 1 });
        println!("{}", const { NUM + 2 });
    }
    
    fn baz() {
        println!("{}", const { N * 2 + 1 });
        println!("{}", const { N * 2 + 2 });
    }
}

The second function compiles but not the first, with the error message:

error[E0401]: can't use generic parameters from outer item
 --> src/lib.rs:5:36
  |
3 | impl<const N: usize> Foo<N> {
  |            - const parameter from outer item
4 |     fn bar() {
5 |         const NUM: usize = const { N * 2 };
  |                                    ^ use of generic parameter from outer item
  |
  = note: a `const` is a separate item from the item that contains it

For more information about this error, try `rustc --explain E0401`.
error: could not compile `playground` (lib) due to 1 previous error

In the inline const stabilisation issue, it says:

Another enhancement that differs from the RFC is that we currently allow inline consts to reference generic parameters. This is implemented in #96557.

which is the case in baz; however, by assigning it to the const NUM, it seems this is no longer the case. I would like to reuse NUM instead of putting in the inline consts every time. Is this a bug?

No, this is documented behaviour:

Const blocks have the ability to reference generic parameters in scope, unlike free constant items.

Const blocks have the ability to reference generic parameters in scope, unlike free constant items.

This is what I want to achieve but can't in bar. It is unable to reference the N generic parameter even though N is in scope.

Yes. The inline const block you use in your assignment to the free NUM constant doesn't have an effect. NUM is still treated as a free constant and thus can't reference N.

2 Likes

I wasn't aware of this, and while I'm sure there is a valid, grounded reason for this being the case¹, I must say from a usability POV it's a fairly unwelcome surprise: it means const { <EXPR> } behaves differently depending on its usage context, which I generally find an undesirable property in language features.

¹ There usually are good reasons when things in rustc don't work as you'd ideally want them to. That said, I see no fundamental reason why a free-but-scoped const item shouldn't be able to access generic parameters from its enclosing item, though in practice it might be as simple as the fact that a free item is essentially modeled the same as any other top level item, except with restrictions w.r.t. what it can be referenced by due to scoping.

There is lots of discussion on this in #104987. I like the quote from this comment on the status quo and why inline consts can refer to scoped stuff but const items can't:

The item form can't reference enclosing stuff, but the inline form can.

2 Likes

Indeed, I guess the next best thing is to put it in a const function and called every time, or the experimental generic_const_items which is also promising.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.