From ac4c3ec87bd2624eb3c1b2f2dfcece4c91b8af88 Mon Sep 17 00:00:00 2001 From: theiz Date: Wed, 26 Nov 2025 15:05:44 -0400 Subject: [PATCH 1/9] const self fields --- text/0000-const-self-fields.md | 256 +++++++++++++++++++++++++++++++++ 1 file changed, 256 insertions(+) create mode 100644 text/0000-const-self-fields.md diff --git a/text/0000-const-self-fields.md b/text/0000-const-self-fields.md new file mode 100644 index 00000000000..bfc5913b4e3 --- /dev/null +++ b/text/0000-const-self-fields.md @@ -0,0 +1,256 @@ +- Feature Name: `const_self_fields` +- Start Date: 2025-11-26 +- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) + +# Summary +[summary]: #summary + +This RFC proposes per-type fields that can be accessed through a value or trait object using a new `const self` syntax: + +```rust +impl Foo{ + const self METADATA_FIELD: i32 = 5; +} +trait Bar { + const self METADATA_FIELD: i32; +} +``` +This allows code like: +```rust +fn use_bar(bar: &dyn Bar) { + let x: i32 = bar.METADATA_FIELD; + let r: &'static i32 = &bar.METADATA_FIELD; +} +fn use_foo(foo: &Foo) { + let x: i32 = foo.METADATA_FIELD; + let r: &'static i32 = &foo.METADATA_FIELD; +} +``` +When combined with traits, enables object-safe, per-implementation constant data that can be read through `&dyn Trait` in a more efficient manner than a dynamic function call, by storing the constant in trait object metadata instead of as a vtable method. +# Motivation +[motivation]: #motivation +Today, Rust has associated constants on types and traits: +```rust +trait Foo { + const VALUE: i32; +} + +impl Foo for MyType { + const VALUE: i32 = 5; +} +``` +For monomorphized code where Self is known, `MyType::VALUE` is an excellent fit. + + +However: You cannot directly read an associated const through a `&dyn Foo`. There is no stable, efficient way to write `foo.VALUE` where `foo: &dyn Foo` and have that dynamically dispatch to the concrete implementation’s const value. + +The common workaround is a vtable method: +```rust +trait Foo { + fn value(&self) -> i32; +} +``` + +This forces a dynamic function call, which is very slow compared to the `const self` equivalent, and does not have as much compiler optimization potential. + +When using a trait object, `const self` stores the bits directly inside the vtable, so accessing it is around as performant as accessing a field from a struct, which is of course, much more performant than a dynamic function call. + +Imagine a hot loop walking over thousands of `&dyn Behavior` objects every frame to read a tiny “flag”. If that’s a virtual method, you pay a dynamic function call on every object. With `const self`, you’re just doing a metadata load, so the per-object overhead is noticeably much smaller. + + + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +## What is const self? + +`const self`introduces metadata fields: constants that belong to a type (or trait implementation) but can be accessed through a `self` expression. + +### Example with a concrete type: + +```rust +struct Foo; + +impl Foo { + const self CONST_FIELD: u32 = 1; +} + +fn write_header(h: &Foo) { + // Reads a per-type constant through a value: + assert_eq!(h.CONST_FIELD, 1); + + // It is a &'static reference + let ptr: &'static u32 = &h.CONST_FIELD; +} +``` + +### Trait objects and metadata fields + +The main power shows up with traits and trait objects: + +```rust +trait Serializer { + // Per-implementation metadata field: + const self FORMAT_VERSION: u32; +} + +struct JsonSerializer; +struct BinarySerializer; + +impl Serializer for JsonSerializer { + const self FORMAT_VERSION: u32 = 1; +} + +impl Serializer for BinarySerializer { + const self FORMAT_VERSION: u32 = 2; +} + +fn write_header(writer: &mut dyn std::io::Write, s: &dyn Serializer) { + // Dynamically picks the implementation’s FORMAT_VERSION + writer.write_all(&[s.FORMAT_VERSION as u8]).unwrap(); +} +``` + +Accessing `FORMAT_VERSION` on a trait object is intended to be as cheap as reading a field from a struct: no virtual call, just a read from the vtable metadata for that trait object. +It is much more efficient than having a `format_version(&self)`, trait method, which does a virtual call. + +On a non trait object, accessing `FORMAT_VERSION` will be as efficient as accessing a `const` value. + +Naming conventions for `const self` fields follow the same conventions as other `const`/associated constants (e.g. `SCREAMING_SNAKE_CASE` as recommended by the Rust style guide); this RFC does not introduce any new naming rules. + +To be more specific about which trait's `const self` field should be accessed, a new `instance.(some_path::Trait.NAME)` syntax can be used. + +NOTE: `T::FIELD` would give a compile-time error when `FIELD` is declared as `const self FIELD: Type`; `const self` fields are only accessible through value syntax (`expr.FIELD`), not type paths. +## How should programmers think about it? + +Programmers can think of `const self` metadata fields as “const but per-type” constants that can be read through references and trait objects, and a replacement for patterns like: +```rust +trait Foo { + fn version(&self) -> u32; // just returns a literal +} +``` +Where the data truly is constant and better modeled as a field in metadata. + +### Teaching differences: new vs existing Rust programmers + +For new Rust programmers, `const self` can be introduced after associated constants: +* Types can have constants: `Type::CONST` +* Sometimes you want those constants visible through trait objects; that’s where `const self` metadata fields come in. +* You can access `self.CONST_FIELD` even if self is `&dyn Trait`, as long as the trait declares it. + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation +## Restrictions +`const self` declarations: +* Must follow the same const-evaluation rules as associated constants (i.e., const expression is evaluated at compile time). +* Are per concrete type (for inherent impls) or per (Trait, ConcreteType) pair for trait implementations. +* The type `T` of a `const self` field must be `Sized` and `'static`, since it is stored in static metadata and references to it have type `&'static T`. + +## Resolution Semantics + + +For a path expression `T::NAME` where `NAME` is a `const self` field on type T, it would give a compiler error. +This is because allowing `T::NAME` syntax would also mean that `dyn Trait::NAME` syntax should be valid, which shouldn't work, since the `dyn Trait` type does not have any information on the `const` value. + +`const self` fields are not simply type-level constants; they are value-accessible metadata. + +For an expression `expr.NAME` where `NAME` is declared as `const self NAME: Type` + +* First, the compiler tries to resolve `NAME` as a normal struct field on the type of expr. +* If that fails, it tries to resolve `NAME` as a `const self` field from: + * inherent impls of the receiver type + * If that fails, it then tries to resolve scoped traits implemented by the receiver type, using the same autoderef/autoref rules as method lookup. +* If both a normal struct field and a const self field of the same name are visible, there would be an ambiguity error, which can be resolved by `expr.(Trait.NAME)` syntax. +* If multiple traits, both implemented by type `T`, provide `const self` fields with the same name and `expr.NAME` is used (where `expr` is an instance of type `T`), that is also an ambiguity error. The programmer must disambiguate using `expr.(Trait.NAME)`. + +## Trait objects + +For a trait object: `&dyn Trait`, where Trait defines: + +```rust +trait Trait { + fn do_something(&self); + const self AGE: i32; +} +``` + +We would have this VTable layout +``` +[0] drop_in_place_fn_ptr +[1] size: usize +[2] align: usize +[3] do_something_fn_ptr +[4] AGE: i32 //stored inline +``` +This layout is conceptual; the precise placement of metadata in the vtable is left as an implementation detail, as long as the observable behavior (one metadata load per access) is preserved. +## Lifetimes + +Taking a reference to a `const self` field always yields a `&'static T`, because the data lives in static metadata +```rust +let p: &'static i32 = &bar.METADATA_FIELD; +``` + +# Drawbacks +[drawbacks]: #drawbacks + +1. Programmers must distinguish: + * Fields (expr.field), + * Associated consts (T::CONST), + * And const fields (expr.METADATA). +2. Vtable layout grows to include inline metadata, which: + * Increases vtable size when heavily used. + * Needs careful specification for any future stable trait-object ABI. +3. Dot syntax now covers both per-instance fields and per-type metadata; tools and docs will need to present these clearly to avoid confusion. + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +## Why this design? +* Explicitly value-only access (expr.NAME) keeps the mental model simple, as it functions similarly to a field access + +* If you have a trait object, you can read its per-impl metadata. + +* If you just have a type, associated consts remain the right tool. + +* By forbidding `T::NAME`, we avoid: + * Confusion over `dyn Trait::NAME`. + * Having to explain when a const is “type-level” vs “metadata-level” under the same syntax. +* A metadata load is cheaper and more predictable than a virtual method call. Especially important when touching many trait objects in tight loops. + +## Why not a macro/library? +A library or macro cannot extend the vtable layout or teach the optimizer that certain values are metadata; it can only generate more methods or global lookup tables. `const self` requires language and compiler support to achieve the desired ergonomics and performance. + +## Alternatives +Keep using methods: +```rust +fn value(&self) -> u32; // remains the standard way. +``` +Downsides: +* Conceptual mismatch (constant-as-method). +* Extra indirection and call overhead. + +# Prior art +[prior-art]: #prior-art + +Rust today: associated consts and const generics: +* Good for type-level, compile-time constants. +* Not designed for dynamic access via trait objects. + +C++ / C# / Java: +* Generally use virtual/virtual-like methods returning constants or static fields accessed via types. +* There is no standard “inline metadata in vtable” feature exposed at the language level for per-implementation constants. + +This RFC can be seen as: +* Making explicit a pattern that compiler and runtimes already rely on internally (metadata attached to vtables). +* Exposing it in a controlled, ergonomic way for user code. +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +* Is there a better declaration syntax than `const self : TYPE`? +* Is `obj.METADATA_FIELD` syntax too conflicting with `obj.normal_field`? +* Is `obj.(Trait.METADATA_FIELD)` a good syntax for disambiguating? +# Future possibilities +[future-possibilities]: #future-possibilities + +* Faster type matching than `dyn Any`: Since `dyn Any` does a virtual call to get the `TypeId`, using `const self` to store the `TypeId` Would be a much more efficient way to downcast. From 2ec65424e129b8da6f380ce20271a740bc901dde Mon Sep 17 00:00:00 2001 From: theiz Date: Wed, 26 Nov 2025 15:16:10 -0400 Subject: [PATCH 2/9] Edited some text --- text/0000-const-self-fields.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/text/0000-const-self-fields.md b/text/0000-const-self-fields.md index bfc5913b4e3..6e8848c4b70 100644 --- a/text/0000-const-self-fields.md +++ b/text/0000-const-self-fields.md @@ -233,13 +233,7 @@ Downsides: # Prior art [prior-art]: #prior-art -Rust today: associated consts and const generics: -* Good for type-level, compile-time constants. -* Not designed for dynamic access via trait objects. - -C++ / C# / Java: -* Generally use virtual/virtual-like methods returning constants or static fields accessed via types. -* There is no standard “inline metadata in vtable” feature exposed at the language level for per-implementation constants. +As of the day this RFC was published, there is no mainstream language with a similar feature. The common workaround is having a virtual function return the literal, but that does not mean we should not strive for a more efficient method. This RFC can be seen as: * Making explicit a pattern that compiler and runtimes already rely on internally (metadata attached to vtables). From a1476c1f153624d1c0871045c9c930d27bfffdcd Mon Sep 17 00:00:00 2001 From: theiz Date: Wed, 26 Nov 2025 15:21:20 -0400 Subject: [PATCH 3/9] Set the ID --- text/{0000-const-self-fields.md => 3888-const-self-fields.md} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename text/{0000-const-self-fields.md => 3888-const-self-fields.md} (98%) diff --git a/text/0000-const-self-fields.md b/text/3888-const-self-fields.md similarity index 98% rename from text/0000-const-self-fields.md rename to text/3888-const-self-fields.md index 6e8848c4b70..5f175655cfd 100644 --- a/text/0000-const-self-fields.md +++ b/text/3888-const-self-fields.md @@ -1,7 +1,7 @@ - Feature Name: `const_self_fields` - Start Date: 2025-11-26 -- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) -- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) +- RFC PR: [rust-lang/rfcs#3888](https://github.com/rust-lang/rfcs/pull/3888) +- Rust Issue: [rust-lang/rust#3888](https://github.com/rust-lang/rust/issues/3888) # Summary [summary]: #summary From cc6e210367feeb6afe957e5008effdd2ed089c84 Mon Sep 17 00:00:00 2001 From: theiz Date: Wed, 26 Nov 2025 15:26:26 -0400 Subject: [PATCH 4/9] Adjusted formatting --- text/3888-const-self-fields.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/text/3888-const-self-fields.md b/text/3888-const-self-fields.md index 5f175655cfd..503971edae7 100644 --- a/text/3888-const-self-fields.md +++ b/text/3888-const-self-fields.md @@ -63,9 +63,9 @@ Imagine a hot loop walking over thousands of `&dyn Behavior` objects every frame # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -## What is const self? +### What is const self? -`const self`introduces metadata fields: constants that belong to a type (or trait implementation) but can be accessed through a `self` expression. +`const self` introduces metadata fields: constants that belong to a type (or trait implementation) but can be accessed through a `self` expression. ### Example with a concrete type: @@ -122,7 +122,7 @@ Naming conventions for `const self` fields follow the same conventions as other To be more specific about which trait's `const self` field should be accessed, a new `instance.(some_path::Trait.NAME)` syntax can be used. NOTE: `T::FIELD` would give a compile-time error when `FIELD` is declared as `const self FIELD: Type`; `const self` fields are only accessible through value syntax (`expr.FIELD`), not type paths. -## How should programmers think about it? +### How should programmers think about it? Programmers can think of `const self` metadata fields as “const but per-type” constants that can be read through references and trait objects, and a replacement for patterns like: ```rust @@ -141,13 +141,13 @@ For new Rust programmers, `const self` can be introduced after associated consta # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -## Restrictions +### Restrictions `const self` declarations: * Must follow the same const-evaluation rules as associated constants (i.e., const expression is evaluated at compile time). * Are per concrete type (for inherent impls) or per (Trait, ConcreteType) pair for trait implementations. * The type `T` of a `const self` field must be `Sized` and `'static`, since it is stored in static metadata and references to it have type `&'static T`. -## Resolution Semantics +### Resolution Semantics For a path expression `T::NAME` where `NAME` is a `const self` field on type T, it would give a compiler error. @@ -164,7 +164,7 @@ For an expression `expr.NAME` where `NAME` is declared as `const self NAME: Type * If both a normal struct field and a const self field of the same name are visible, there would be an ambiguity error, which can be resolved by `expr.(Trait.NAME)` syntax. * If multiple traits, both implemented by type `T`, provide `const self` fields with the same name and `expr.NAME` is used (where `expr` is an instance of type `T`), that is also an ambiguity error. The programmer must disambiguate using `expr.(Trait.NAME)`. -## Trait objects +### Trait objects For a trait object: `&dyn Trait`, where Trait defines: @@ -184,7 +184,7 @@ We would have this VTable layout [4] AGE: i32 //stored inline ``` This layout is conceptual; the precise placement of metadata in the vtable is left as an implementation detail, as long as the observable behavior (one metadata load per access) is preserved. -## Lifetimes +### Lifetimes Taking a reference to a `const self` field always yields a `&'static T`, because the data lives in static metadata ```rust @@ -206,7 +206,7 @@ let p: &'static i32 = &bar.METADATA_FIELD; # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -## Why this design? +### Why this design? * Explicitly value-only access (expr.NAME) keeps the mental model simple, as it functions similarly to a field access * If you have a trait object, you can read its per-impl metadata. @@ -218,10 +218,10 @@ let p: &'static i32 = &bar.METADATA_FIELD; * Having to explain when a const is “type-level” vs “metadata-level” under the same syntax. * A metadata load is cheaper and more predictable than a virtual method call. Especially important when touching many trait objects in tight loops. -## Why not a macro/library? +### Why not a macro/library? A library or macro cannot extend the vtable layout or teach the optimizer that certain values are metadata; it can only generate more methods or global lookup tables. `const self` requires language and compiler support to achieve the desired ergonomics and performance. -## Alternatives +### Alternatives Keep using methods: ```rust fn value(&self) -> u32; // remains the standard way. From 726992d797c32fa0da18659a00ee82ebaf784dd0 Mon Sep 17 00:00:00 2001 From: theiz Date: Wed, 26 Nov 2025 15:32:08 -0400 Subject: [PATCH 5/9] fixed some naming and syntax --- text/3888-const-self-fields.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/3888-const-self-fields.md b/text/3888-const-self-fields.md index 503971edae7..18e86a8a1ac 100644 --- a/text/3888-const-self-fields.md +++ b/text/3888-const-self-fields.md @@ -166,7 +166,7 @@ For an expression `expr.NAME` where `NAME` is declared as `const self NAME: Type ### Trait objects -For a trait object: `&dyn Trait`, where Trait defines: +For a trait object: `&dyn Trait`, where `Trait` defines: ```rust trait Trait { @@ -241,7 +241,7 @@ This RFC can be seen as: # Unresolved questions [unresolved-questions]: #unresolved-questions -* Is there a better declaration syntax than `const self : TYPE`? +* Is there a better declaration syntax than `const self NAME : Type`? * Is `obj.METADATA_FIELD` syntax too conflicting with `obj.normal_field`? * Is `obj.(Trait.METADATA_FIELD)` a good syntax for disambiguating? # Future possibilities From 19700e913e257584056411fca3bb20b70b825cbc Mon Sep 17 00:00:00 2001 From: theiz Date: Wed, 26 Nov 2025 21:10:28 -0400 Subject: [PATCH 6/9] Added a `Freeze` requirement to const self fields --- text/3888-const-self-fields.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/text/3888-const-self-fields.md b/text/3888-const-self-fields.md index 18e86a8a1ac..5d8fcbe81b7 100644 --- a/text/3888-const-self-fields.md +++ b/text/3888-const-self-fields.md @@ -42,7 +42,6 @@ impl Foo for MyType { ``` For monomorphized code where Self is known, `MyType::VALUE` is an excellent fit. - However: You cannot directly read an associated const through a `&dyn Foo`. There is no stable, efficient way to write `foo.VALUE` where `foo: &dyn Foo` and have that dynamically dispatch to the concrete implementation’s const value. The common workaround is a vtable method: @@ -65,7 +64,7 @@ Imagine a hot loop walking over thousands of `&dyn Behavior` objects every frame ### What is const self? -`const self` introduces metadata fields: constants that belong to a type (or trait implementation) but can be accessed through a `self` expression. +`const self` introduces metadata fields: constants that belong to a type (or trait implementation) but are accessed through a `self` expression. ### Example with a concrete type: @@ -85,6 +84,8 @@ fn write_header(h: &Foo) { } ``` +A `const self` field's type must implement `Freeze`, must be `'static`, and must be `Sized`. These rules are enforced by the compiler. + ### Trait objects and metadata fields The main power shows up with traits and trait objects: @@ -121,7 +122,7 @@ Naming conventions for `const self` fields follow the same conventions as other To be more specific about which trait's `const self` field should be accessed, a new `instance.(some_path::Trait.NAME)` syntax can be used. -NOTE: `T::FIELD` would give a compile-time error when `FIELD` is declared as `const self FIELD: Type`; `const self` fields are only accessible through value syntax (`expr.FIELD`), not type paths. +`T::FIELD` would give a compile-time error when `FIELD` is declared as `const self FIELD: Type`; `const self` fields are only accessible through value syntax (`expr.FIELD`), not type paths. ### How should programmers think about it? Programmers can think of `const self` metadata fields as “const but per-type” constants that can be read through references and trait objects, and a replacement for patterns like: @@ -143,14 +144,14 @@ For new Rust programmers, `const self` can be introduced after associated consta [reference-level-explanation]: #reference-level-explanation ### Restrictions `const self` declarations: -* Must follow the same const-evaluation rules as associated constants (i.e., const expression is evaluated at compile time). +* Can only be assigned to constant expressions. * Are per concrete type (for inherent impls) or per (Trait, ConcreteType) pair for trait implementations. -* The type `T` of a `const self` field must be `Sized` and `'static`, since it is stored in static metadata and references to it have type `&'static T`. +* The type `T` of a `const self` field must be `Sized`, `Freeze`, and `'static` to prevent unsoundness. ### Resolution Semantics -For a path expression `T::NAME` where `NAME` is a `const self` field on type T, it would give a compiler error. +For a path expression `T::NAME` where `NAME` is a `const self` field on type T, it would give a compiler error. This is because allowing `T::NAME` syntax would also mean that `dyn Trait::NAME` syntax should be valid, which shouldn't work, since the `dyn Trait` type does not have any information on the `const` value. `const self` fields are not simply type-level constants; they are value-accessible metadata. @@ -186,11 +187,11 @@ We would have this VTable layout This layout is conceptual; the precise placement of metadata in the vtable is left as an implementation detail, as long as the observable behavior (one metadata load per access) is preserved. ### Lifetimes -Taking a reference to a `const self` field always yields a `&'static T`, because the data lives in static metadata +Taking a reference to a `const self` field always yields a `&'static T`. This is sound since `const self` types are required to implement `Freeze`, and are required to be `'static`. ```rust let p: &'static i32 = &bar.METADATA_FIELD; ``` - +However, you get a potentially different `'static` reference every time you use the same `const self` field from the same type. This is because the storage for a `const self` field potentially lives in a trait object’s metadata, and different trait objects of the same underlying type do not necessarily share the same exact metadata. # Drawbacks [drawbacks]: #drawbacks From aad6bfff764ec3da08eb52393ade9ec2a36f0abe Mon Sep 17 00:00:00 2001 From: theiz Date: Wed, 26 Nov 2025 21:12:00 -0400 Subject: [PATCH 7/9] fixed typo --- text/3888-const-self-fields.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/3888-const-self-fields.md b/text/3888-const-self-fields.md index 5d8fcbe81b7..a453d3b57a8 100644 --- a/text/3888-const-self-fields.md +++ b/text/3888-const-self-fields.md @@ -151,7 +151,7 @@ For new Rust programmers, `const self` can be introduced after associated consta ### Resolution Semantics -For a path expression `T::NAME` where `NAME` is a `const self` field on type T, it would give a compiler error. +For a path expression `T::NAME` where `NAME` is a `const self` field of type T, it would give a compiler error. This is because allowing `T::NAME` syntax would also mean that `dyn Trait::NAME` syntax should be valid, which shouldn't work, since the `dyn Trait` type does not have any information on the `const` value. `const self` fields are not simply type-level constants; they are value-accessible metadata. From 484c5b04529aa0d00152dd7f4b573baede09d278 Mon Sep 17 00:00:00 2001 From: theiz Date: Thu, 27 Nov 2025 15:27:39 -0400 Subject: [PATCH 8/9] Added `static const self`, and changed how the normal `const self` works Adjusted ambiguity rules a bit --- text/3888-const-self-fields.md | 136 ++++++++++++++++++++++++--------- 1 file changed, 102 insertions(+), 34 deletions(-) diff --git a/text/3888-const-self-fields.md b/text/3888-const-self-fields.md index a453d3b57a8..7204197501f 100644 --- a/text/3888-const-self-fields.md +++ b/text/3888-const-self-fields.md @@ -6,25 +6,27 @@ # Summary [summary]: #summary -This RFC proposes per-type fields that can be accessed through a value or trait object using a new `const self` syntax: +This RFC proposes per-type fields that can be accessed through a value or trait object using new `const self` and `static const self` syntax: ```rust impl Foo{ const self METADATA_FIELD: i32 = 5; + static const self STATIC_METADATA_FIELD: i32 = 10; } trait Bar { const self METADATA_FIELD: i32; + static const self STATIC_METADATA_FIELD: i32; } ``` This allows code like: ```rust fn use_bar(bar: &dyn Bar) { - let x: i32 = bar.METADATA_FIELD; - let r: &'static i32 = &bar.METADATA_FIELD; + let x: i32 = bar.METADATA_FIELD; // const self + let y: &'static i32 = &bar.STATIC_METADATA_FIELD; // static const self } fn use_foo(foo: &Foo) { - let x: i32 = foo.METADATA_FIELD; - let r: &'static i32 = &foo.METADATA_FIELD; + let x: i32 = foo.METADATA_FIELD; // const self + let y: &'static i32 = &foo.STATIC_METADATA_FIELD; // static const self } ``` When combined with traits, enables object-safe, per-implementation constant data that can be read through `&dyn Trait` in a more efficient manner than a dynamic function call, by storing the constant in trait object metadata instead of as a vtable method. @@ -40,7 +42,7 @@ impl Foo for MyType { const VALUE: i32 = 5; } ``` -For monomorphized code where Self is known, `MyType::VALUE` is an excellent fit. +For monomorphized code where `Self` is known, `MyType::VALUE` is an excellent fit. However: You cannot directly read an associated const through a `&dyn Foo`. There is no stable, efficient way to write `foo.VALUE` where `foo: &dyn Foo` and have that dynamically dispatch to the concrete implementation’s const value. @@ -51,11 +53,11 @@ trait Foo { } ``` -This forces a dynamic function call, which is very slow compared to the `const self` equivalent, and does not have as much compiler optimization potential. +This forces a dynamic function call, which is very slow compared to the `const self` and `static const self` equivalent, and does not have as much compiler optimization potential. -When using a trait object, `const self` stores the bits directly inside the vtable, so accessing it is around as performant as accessing a field from a struct, which is of course, much more performant than a dynamic function call. +When using a trait object, `const self` and `static const self` store the bits directly inside the vtable, so accessing it is around as performant as accessing a field from a struct, which is of course, much more performant than a dynamic function call. -Imagine a hot loop walking over thousands of `&dyn Behavior` objects every frame to read a tiny “flag”. If that’s a virtual method, you pay a dynamic function call on every object. With `const self`, you’re just doing a metadata load, so the per-object overhead is noticeably much smaller. +Imagine a hot loop walking over thousands of `&dyn Behavior` objects every frame to read a tiny “flag”. If that’s a virtual method, you pay a dynamic function call on every object. With `const self` and `static const self`, you’re just doing a metadata load, so the per-object overhead is noticeably much smaller. @@ -66,7 +68,7 @@ Imagine a hot loop walking over thousands of `&dyn Behavior` objects every frame `const self` introduces metadata fields: constants that belong to a type (or trait implementation) but are accessed through a `self` expression. -### Example with a concrete type: +Example: ```rust struct Foo; @@ -78,13 +80,51 @@ impl Foo { fn write_header(h: &Foo) { // Reads a per-type constant through a value: assert_eq!(h.CONST_FIELD, 1); + let value: u32 = h.CONST_FIELD; +} +``` + +A `const self` field's type can have interior mutability, because the compiler does not operate on the field directly by its reference, even if it is stored in a trait object's metadata. +It first copies the field, and does the operations on that copied value, similar to how `const` variables work in rust. +This makes using it with interior mutability sound. + +When using references like shown below: + +```rust +let value : &u32 = &h.CONST_FIELD; +``` + +This works similarly to how `const` variables work in Rust: it copies `CONST_FIELD`, then takes a reference to that copy. Unlike a normal `const` item though, the resulting reference does **not** have a `'static` lifetime; it has a temporary lifetime, as if you had written: + +```rust +let tmp: u32 = h.CONST_FIELD; // copied +let value: &u32 = &tmp; +``` +### What is static const self - // It is a &'static reference - let ptr: &'static u32 = &h.CONST_FIELD; + +`static const self` is similar to `const self`, however, working on `static const self` fields means working directly with its reference. Think: global `static` variable you are not allowed to mutate. +This means that the type of a `static const self` field must not have any interior mutability. In other words, the type of the field must implement `Freeze`. This is enforced by the compiler. + +Example: + +```rust +struct Foo; + +impl Foo { + static const self STATIC_CONST_FIELD: u32 = 1; +} + +fn write_header(h: &Foo) { + // Reads a per-type constant through a value: + assert_eq!(h.STATIC_CONST_FIELD, 1); + let reference: &'static u32 = &h.STATIC_CONST_FIELD; } ``` +`static const self` field's references have `'static` lifetimes. + +Note that unlike normal `static` variables, you cannot rely on the reference of a `static const self` field to be the same reference of the same `static const self` field of the same underlying type. -A `const self` field's type must implement `Freeze`, must be `'static`, and must be `Sized`. These rules are enforced by the compiler. ### Trait objects and metadata fields @@ -118,14 +158,14 @@ It is much more efficient than having a `format_version(&self)`, trait method, w On a non trait object, accessing `FORMAT_VERSION` will be as efficient as accessing a `const` value. -Naming conventions for `const self` fields follow the same conventions as other `const`/associated constants (e.g. `SCREAMING_SNAKE_CASE` as recommended by the Rust style guide); this RFC does not introduce any new naming rules. +Naming conventions for `const self` and `static const self` fields follow the same conventions as other `const` and `static` variables (e.g. `SCREAMING_SNAKE_CASE` as recommended by the Rust style guide); this RFC does not introduce any new naming rules. -To be more specific about which trait's `const self` field should be accessed, a new `instance.(some_path::Trait.NAME)` syntax can be used. +To be more specific about which trait's `const self`/`static const self` field should be accessed, a new `instance.(some_path::Trait.NAME)` syntax can be used. -`T::FIELD` would give a compile-time error when `FIELD` is declared as `const self FIELD: Type`; `const self` fields are only accessible through value syntax (`expr.FIELD`), not type paths. +`T::FIELD` would give a compile-time error when `FIELD` is a `static const self` or `const self` field. These fields are only accessible through value syntax (`expr.FIELD`), not type paths. ### How should programmers think about it? -Programmers can think of `const self` metadata fields as “const but per-type” constants that can be read through references and trait objects, and a replacement for patterns like: +Programmers can think of `const self`/`static const self` metadata fields as “const but per-type” constants that can be read through references and trait objects, and a replacement for patterns like: ```rust trait Foo { fn version(&self) -> u32; // just returns a literal @@ -135,35 +175,61 @@ Where the data truly is constant and better modeled as a field in metadata. ### Teaching differences: new vs existing Rust programmers -For new Rust programmers, `const self` can be introduced after associated constants: +For new Rust programmers, `const self` and `static const self` can be introduced after associated constants: * Types can have constants: `Type::CONST` * Sometimes you want those constants visible through trait objects; that’s where `const self` metadata fields come in. +* Sometimes you want to be able to directly reference those constants. Good for when it is too large; that's where `static const self` metadata fields come in. * You can access `self.CONST_FIELD` even if self is `&dyn Trait`, as long as the trait declares it. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation ### Restrictions -`const self` declarations: -* Can only be assigned to constant expressions. -* Are per concrete type (for inherent impls) or per (Trait, ConcreteType) pair for trait implementations. -* The type `T` of a `const self` field must be `Sized`, `Freeze`, and `'static` to prevent unsoundness. + +For `const self`, we only ever operate on copies, so its type having interior mutability is fine. + +For `static const self`, we get a `&'static T` directly from the metadata; to keep that sound we additionally require `T: Freeze` so that `&T` truly represents immutable data. + +Both `const self` and `static const self` field's type are required to be `Sized`, and must have a `'static` lifetime. + +Assume we have: + +```rust +struct Foo; +impl Foo{ + const self X: Type = value; + static const self Y: OtherType = value; +} +``` + +then it can be used like: + +```rust + +let variable = obj.X; //ok. Copies it +let variable2 : &_ = &obj.X; // ok, but what it actually does is copy it, and uses the reference of the copy. Reference lifetime is not 'static. + + +let variable3 = obj.Y; // ok if the type of 'Y' implements Copy +let variable4 : &'static _ = &obj.Y; // ok. Lifetime of reference is 'static, uses the reference directly +``` + ### Resolution Semantics -For a path expression `T::NAME` where `NAME` is a `const self` field of type T, it would give a compiler error. +For a path expression `T::NAME` where `NAME` is a `const self` or `static const self` field of type `T`, it would give a compiler error. This is because allowing `T::NAME` syntax would also mean that `dyn Trait::NAME` syntax should be valid, which shouldn't work, since the `dyn Trait` type does not have any information on the `const` value. -`const self` fields are not simply type-level constants; they are value-accessible metadata. +`const self` and `static const self` fields are not simply type-level constants; they are value-accessible metadata. -For an expression `expr.NAME` where `NAME` is declared as `const self NAME: Type` +For an expression `expr.NAME` where `NAME` is declared as `const self NAME: Type` or `static const self NAME: Type`: * First, the compiler tries to resolve `NAME` as a normal struct field on the type of expr. -* If that fails, it tries to resolve `NAME` as a `const self` field from: +* If that fails, it tries to resolve `NAME` as a `const self`/`static const self` field from: * inherent impls of the receiver type * If that fails, it then tries to resolve scoped traits implemented by the receiver type, using the same autoderef/autoref rules as method lookup. -* If both a normal struct field and a const self field of the same name are visible, there would be an ambiguity error, which can be resolved by `expr.(Trait.NAME)` syntax. -* If multiple traits, both implemented by type `T`, provide `const self` fields with the same name and `expr.NAME` is used (where `expr` is an instance of type `T`), that is also an ambiguity error. The programmer must disambiguate using `expr.(Trait.NAME)`. +* A struct cannot have a normal field and an inherent `const self`/`static const self` field with the same name. +* If multiple traits, both implemented by type `T` and are in scope, provide `const self` or `static const self` fields with the same name and `expr.NAME` is used (where `expr` is an instance of type `T`), that is also an ambiguity error. The programmer must disambiguate using `expr.(Trait.NAME)`. ### Trait objects @@ -173,6 +239,7 @@ For a trait object: `&dyn Trait`, where `Trait` defines: trait Trait { fn do_something(&self); const self AGE: i32; + const self LARGE_VALUE: LargeType; } ``` @@ -183,15 +250,16 @@ We would have this VTable layout [2] align: usize [3] do_something_fn_ptr [4] AGE: i32 //stored inline +[5] LARGE_VALUE: LargeType //stored inline ``` This layout is conceptual; the precise placement of metadata in the vtable is left as an implementation detail, as long as the observable behavior (one metadata load per access) is preserved. ### Lifetimes -Taking a reference to a `const self` field always yields a `&'static T`. This is sound since `const self` types are required to implement `Freeze`, and are required to be `'static`. +Taking a reference to a `static const self` field always yields a `&'static T`. This is sound since `static const self` types are required to implement `Freeze`, and are required to be `'static`. ```rust -let p: &'static i32 = &bar.METADATA_FIELD; +let p: &'static i32 = &bar.STATIC_METADATA_FIELD; ``` -However, you get a potentially different `'static` reference every time you use the same `const self` field from the same type. This is because the storage for a `const self` field potentially lives in a trait object’s metadata, and different trait objects of the same underlying type do not necessarily share the same exact metadata. +However, you get a potentially different `'static` reference every time you use the same `static const self` field from the same type. This is because the storage for a `static const self` field potentially lives in a trait object’s metadata, and different trait objects of the same underlying type do not necessarily share the same exact metadata. # Drawbacks [drawbacks]: #drawbacks @@ -220,7 +288,7 @@ However, you get a potentially different `'static` reference every time you use * A metadata load is cheaper and more predictable than a virtual method call. Especially important when touching many trait objects in tight loops. ### Why not a macro/library? -A library or macro cannot extend the vtable layout or teach the optimizer that certain values are metadata; it can only generate more methods or global lookup tables. `const self` requires language and compiler support to achieve the desired ergonomics and performance. +A library or macro cannot extend the vtable layout or teach the optimizer that certain values are metadata; it can only generate more methods or global lookup tables. `const self`/`static const self` requires language and compiler support to achieve the desired ergonomics and performance. ### Alternatives Keep using methods: @@ -242,10 +310,10 @@ This RFC can be seen as: # Unresolved questions [unresolved-questions]: #unresolved-questions -* Is there a better declaration syntax than `const self NAME : Type`? +* Is there a better declaration syntax than `static const self NAME : Type`/`const self NAME : Type`? * Is `obj.METADATA_FIELD` syntax too conflicting with `obj.normal_field`? * Is `obj.(Trait.METADATA_FIELD)` a good syntax for disambiguating? # Future possibilities [future-possibilities]: #future-possibilities -* Faster type matching than `dyn Any`: Since `dyn Any` does a virtual call to get the `TypeId`, using `const self` to store the `TypeId` Would be a much more efficient way to downcast. +* Faster type matching than `dyn Any`: Since `dyn Any` does a virtual call to get the `TypeId`, using `static const self` to store the `TypeId` would be a much more efficient way to downcast. From 3d4c7557571dbfbbb287a45f88b9f3944582229e Mon Sep 17 00:00:00 2001 From: theiz Date: Sun, 30 Nov 2025 00:22:37 -0400 Subject: [PATCH 9/9] renamed `static const self` to `const self ref` --- text/3888-const-self-fields.md | 76 +++++++++++++++++----------------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/text/3888-const-self-fields.md b/text/3888-const-self-fields.md index 7204197501f..63399a0699b 100644 --- a/text/3888-const-self-fields.md +++ b/text/3888-const-self-fields.md @@ -6,27 +6,27 @@ # Summary [summary]: #summary -This RFC proposes per-type fields that can be accessed through a value or trait object using new `const self` and `static const self` syntax: +This RFC proposes per-type fields that can be accessed through a value or trait object using new `const self` and `const self ref` syntax: ```rust impl Foo{ const self METADATA_FIELD: i32 = 5; - static const self STATIC_METADATA_FIELD: i32 = 10; + const self ref REF_METADATA_FIELD: i32 = 10; } trait Bar { const self METADATA_FIELD: i32; - static const self STATIC_METADATA_FIELD: i32; + const self ref REF_METADATA_FIELD: i32; } ``` This allows code like: ```rust fn use_bar(bar: &dyn Bar) { let x: i32 = bar.METADATA_FIELD; // const self - let y: &'static i32 = &bar.STATIC_METADATA_FIELD; // static const self + let y: &'static i32 = &bar.REF_METADATA_FIELD; // const self ref } fn use_foo(foo: &Foo) { let x: i32 = foo.METADATA_FIELD; // const self - let y: &'static i32 = &foo.STATIC_METADATA_FIELD; // static const self + let y: &'static i32 = &foo.REF_METADATA_FIELD; // const self ref } ``` When combined with traits, enables object-safe, per-implementation constant data that can be read through `&dyn Trait` in a more efficient manner than a dynamic function call, by storing the constant in trait object metadata instead of as a vtable method. @@ -53,11 +53,11 @@ trait Foo { } ``` -This forces a dynamic function call, which is very slow compared to the `const self` and `static const self` equivalent, and does not have as much compiler optimization potential. +This forces a dynamic function call, which is very slow compared to the `const self` and `const self ref` equivalent, and does not have as much compiler optimization potential. -When using a trait object, `const self` and `static const self` store the bits directly inside the vtable, so accessing it is around as performant as accessing a field from a struct, which is of course, much more performant than a dynamic function call. +When using a trait object, `const self` and `const self ref` store the bits directly inside the vtable, so accessing it is around as performant as accessing a field from a struct, which is of course, much more performant than a dynamic function call. -Imagine a hot loop walking over thousands of `&dyn Behavior` objects every frame to read a tiny “flag”. If that’s a virtual method, you pay a dynamic function call on every object. With `const self` and `static const self`, you’re just doing a metadata load, so the per-object overhead is noticeably much smaller. +Imagine a hot loop walking over thousands of `&dyn Behavior` objects every frame to read a tiny “flag”. If that’s a virtual method, you pay a dynamic function call on every object. With `const self` and `const self ref`, you’re just doing a metadata load, so the per-object overhead is noticeably much smaller. @@ -100,11 +100,11 @@ This works similarly to how `const` variables work in Rust: it copies `CONST_FIE let tmp: u32 = h.CONST_FIELD; // copied let value: &u32 = &tmp; ``` -### What is static const self +### What is const self ref -`static const self` is similar to `const self`, however, working on `static const self` fields means working directly with its reference. Think: global `static` variable you are not allowed to mutate. -This means that the type of a `static const self` field must not have any interior mutability. In other words, the type of the field must implement `Freeze`. This is enforced by the compiler. +`const self ref` is similar to `const self`, however, working on `const self ref` fields means working directly with its shared reference (no `mut` access). +The type of a `const self ref` field must not have any interior mutability to ensure soundness. In other words, the type of the field must implement `Freeze`. This is enforced by the compiler. Example: @@ -112,18 +112,18 @@ Example: struct Foo; impl Foo { - static const self STATIC_CONST_FIELD: u32 = 1; + const self ref REF_CONST_FIELD: u32 = 1; } fn write_header(h: &Foo) { // Reads a per-type constant through a value: - assert_eq!(h.STATIC_CONST_FIELD, 1); - let reference: &'static u32 = &h.STATIC_CONST_FIELD; + assert_eq!(h.REF_CONST_FIELD, 1); + let reference: &'static u32 = &h.REF_CONST_FIELD; } ``` -`static const self` field's references have `'static` lifetimes. +`const self ref` field's references have `'static` lifetimes. -Note that unlike normal `static` variables, you cannot rely on the reference of a `static const self` field to be the same reference of the same `static const self` field of the same underlying type. +Note that unlike normal `static` variables, you cannot rely on the reference of a `const self ref` field to be the same reference of the same `const self ref` field of the same underlying type. ### Trait objects and metadata fields @@ -158,14 +158,14 @@ It is much more efficient than having a `format_version(&self)`, trait method, w On a non trait object, accessing `FORMAT_VERSION` will be as efficient as accessing a `const` value. -Naming conventions for `const self` and `static const self` fields follow the same conventions as other `const` and `static` variables (e.g. `SCREAMING_SNAKE_CASE` as recommended by the Rust style guide); this RFC does not introduce any new naming rules. +Naming conventions for `const self` and `const self ref` fields follow the same conventions as other `const` and `static` variables (e.g. `SCREAMING_SNAKE_CASE` as recommended by the Rust style guide); this RFC does not introduce any new naming rules. -To be more specific about which trait's `const self`/`static const self` field should be accessed, a new `instance.(some_path::Trait.NAME)` syntax can be used. +To be more specific about which trait's `const self`/`const self ref` field should be accessed, a new `instance.(some_path::Trait.NAME)` syntax can be used. -`T::FIELD` would give a compile-time error when `FIELD` is a `static const self` or `const self` field. These fields are only accessible through value syntax (`expr.FIELD`), not type paths. +`T::FIELD` would give a compile-time error when `FIELD` is a `const self ref` or `const self` field. These fields are only accessible through value syntax (`expr.FIELD`), not type paths. ### How should programmers think about it? -Programmers can think of `const self`/`static const self` metadata fields as “const but per-type” constants that can be read through references and trait objects, and a replacement for patterns like: +Programmers can think of `const self`/`const self ref` metadata fields as “const but per-type” constants that can be read through references and trait objects, and a replacement for patterns like: ```rust trait Foo { fn version(&self) -> u32; // just returns a literal @@ -175,21 +175,21 @@ Where the data truly is constant and better modeled as a field in metadata. ### Teaching differences: new vs existing Rust programmers -For new Rust programmers, `const self` and `static const self` can be introduced after associated constants: +For new Rust programmers, `const self` and `const self ref` can be introduced after associated constants: * Types can have constants: `Type::CONST` * Sometimes you want those constants visible through trait objects; that’s where `const self` metadata fields come in. -* Sometimes you want to be able to directly reference those constants. Good for when it is too large; that's where `static const self` metadata fields come in. +* Sometimes you want to be able to directly reference those constants. Good for when it is too large; that's where `const self ref` metadata fields come in. * You can access `self.CONST_FIELD` even if self is `&dyn Trait`, as long as the trait declares it. # Reference-level explanation [reference-level-explanation]: #reference-level-explanation ### Restrictions -For `const self`, we only ever operate on copies, so its type having interior mutability is fine. +For `const self FOO: T = ..;`, we only ever operate on copies, so its type having interior mutability is fine. -For `static const self`, we get a `&'static T` directly from the metadata; to keep that sound we additionally require `T: Freeze` so that `&T` truly represents immutable data. +For `const self ref FOO: T = ..;`, we get a `&'static T` directly from the metadata; to keep that sound we additionally require `T: Freeze` so that `&T` truly represents immutable data. -Both `const self` and `static const self` field's type are required to be `Sized`, and must have a `'static` lifetime. +Both `const self` and `const self ref` field's type are required to be `Sized`, and must have a `'static` lifetime . Assume we have: @@ -197,7 +197,7 @@ Assume we have: struct Foo; impl Foo{ const self X: Type = value; - static const self Y: OtherType = value; + const self ref Y: OtherType = value; } ``` @@ -217,19 +217,19 @@ let variable4 : &'static _ = &obj.Y; // ok. Lifetime of reference is 'static, us ### Resolution Semantics -For a path expression `T::NAME` where `NAME` is a `const self` or `static const self` field of type `T`, it would give a compiler error. +For a path expression `T::NAME` where `NAME` is a `const self` or `const self ref` field of type `T`, it would give a compiler error. This is because allowing `T::NAME` syntax would also mean that `dyn Trait::NAME` syntax should be valid, which shouldn't work, since the `dyn Trait` type does not have any information on the `const` value. -`const self` and `static const self` fields are not simply type-level constants; they are value-accessible metadata. +`const self` and `const self ref` fields are not simply type-level constants; they are value-accessible metadata. -For an expression `expr.NAME` where `NAME` is declared as `const self NAME: Type` or `static const self NAME: Type`: +For an expression `expr.NAME` where `NAME` is declared as `const self NAME: Type` or `const self ref NAME: Type`: * First, the compiler tries to resolve `NAME` as a normal struct field on the type of expr. -* If that fails, it tries to resolve `NAME` as a `const self`/`static const self` field from: +* If that fails, it tries to resolve `NAME` as a `const self`/`const self ref` field from: * inherent impls of the receiver type * If that fails, it then tries to resolve scoped traits implemented by the receiver type, using the same autoderef/autoref rules as method lookup. -* A struct cannot have a normal field and an inherent `const self`/`static const self` field with the same name. -* If multiple traits, both implemented by type `T` and are in scope, provide `const self` or `static const self` fields with the same name and `expr.NAME` is used (where `expr` is an instance of type `T`), that is also an ambiguity error. The programmer must disambiguate using `expr.(Trait.NAME)`. +* A struct cannot have a normal field and an inherent `const self`/`const self ref` field with the same name. +* If multiple traits, both implemented by type `T` and are in scope, provide `const self` or `const self ref` fields with the same name and `expr.NAME` is used (where `expr` is an instance of type `T`), that is also an ambiguity error. The programmer must disambiguate using `expr.(Trait.NAME)`. ### Trait objects @@ -255,11 +255,11 @@ We would have this VTable layout This layout is conceptual; the precise placement of metadata in the vtable is left as an implementation detail, as long as the observable behavior (one metadata load per access) is preserved. ### Lifetimes -Taking a reference to a `static const self` field always yields a `&'static T`. This is sound since `static const self` types are required to implement `Freeze`, and are required to be `'static`. +Taking a reference to a `const self ref` field always yields a `&'static T`. This is sound since `const self ref` types are required to implement `Freeze`, are required to be `'static`, and only provide a shared reference (you cannot get a mutable reference to it) ```rust -let p: &'static i32 = &bar.STATIC_METADATA_FIELD; +let p: &'static i32 = &bar.REF_METADATA_FIELD; ``` -However, you get a potentially different `'static` reference every time you use the same `static const self` field from the same type. This is because the storage for a `static const self` field potentially lives in a trait object’s metadata, and different trait objects of the same underlying type do not necessarily share the same exact metadata. +However, you get a potentially different `'static` reference every time you use the same `const self ref` field from the same type. This is because the storage for a `const self ref` field potentially lives in a trait object’s metadata, and different trait objects of the same underlying type do not necessarily share the same exact metadata. # Drawbacks [drawbacks]: #drawbacks @@ -288,7 +288,7 @@ However, you get a potentially different `'static` reference every time you use * A metadata load is cheaper and more predictable than a virtual method call. Especially important when touching many trait objects in tight loops. ### Why not a macro/library? -A library or macro cannot extend the vtable layout or teach the optimizer that certain values are metadata; it can only generate more methods or global lookup tables. `const self`/`static const self` requires language and compiler support to achieve the desired ergonomics and performance. +A library or macro cannot extend the vtable layout or teach the optimizer that certain values are metadata; it can only generate more methods or global lookup tables. `const self`/`const self ref` requires language and compiler support to achieve the desired ergonomics and performance. ### Alternatives Keep using methods: @@ -310,10 +310,10 @@ This RFC can be seen as: # Unresolved questions [unresolved-questions]: #unresolved-questions -* Is there a better declaration syntax than `static const self NAME : Type`/`const self NAME : Type`? +* Is there a better declaration syntax than `const self ref NAME : Type`/`const self NAME : Type`? * Is `obj.METADATA_FIELD` syntax too conflicting with `obj.normal_field`? * Is `obj.(Trait.METADATA_FIELD)` a good syntax for disambiguating? # Future possibilities [future-possibilities]: #future-possibilities -* Faster type matching than `dyn Any`: Since `dyn Any` does a virtual call to get the `TypeId`, using `static const self` to store the `TypeId` would be a much more efficient way to downcast. +* Faster type matching than `dyn Any`: Since `dyn Any` does a virtual call to get the `TypeId`, using `const self ref` to store the `TypeId` would be a much more efficient way to downcast.