From 02b547a3d0614dbb2a73a78d3c098641601ffd88 Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Thu, 21 Dec 2023 04:16:19 -0800 Subject: [PATCH 01/19] new RFC: const_type_inference --- text/0000-const-type-inference.md | 114 ++++++++++++++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 text/0000-const-type-inference.md diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md new file mode 100644 index 00000000000..f0a601750a7 --- /dev/null +++ b/text/0000-const-type-inference.md @@ -0,0 +1,114 @@ +- Feature Name: const_type_inference +- Start Date: 2023-12-21 +- 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 + +Allow type inference for `const` or `static` when the type of the initial value is known. + +# Motivation +[motivation]: #motivation + +Rust currently requires explicit type annotations for `const` and `static` items. + + +In simple cases, explicitly writing out +the type of the const seems trivial. However, this isn't always the case: + +- Sometimes the constant's value is complex, making the explicit type overly verbose. +- In some cases, the type may be unnameable. +- When creating macros, the precise type might not be known to the macro author. +- Code generators may not have enough information to easily determine the type. + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +You may declare constants and static variables without specifying their types when the type can be inferred +from the initial value. For example: + +```rs +const PI = 3.1415; // inferred as f64 +static MESSAGE = "Hello, World!"; // inferred as &'static str +const FN_PTR = std::string::String::default; // inferred as fn() -> String +``` + +This change aims to make Rust code more concise and maintainable, especially in scenarios where the types of +const items are complicated or not easily expressible. + + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + + +The type inference for `const` and `static` will leverage Rust's existing type inference mechanisms. The compiler will infer the type exclusively based on the RHS. If the type cannot be determined or if it leads to ambiguities, the compiler will emit an error, prompting the programmer to specify the type explicitly. + + +Today, the compiler already gives hint for most cases where the const or static item is missing a type: + +``` +802 | const A = 0; + | ^ help: provide a type for the constant: `: i32` +``` + + +``` +error: missing type for `const` item + --> file.rs:27:26 + | +27 | pub const update_blas = SystemStage { system: test_system, stage: vk::Pipeli... + | ^ help: provide a type for the constant: `: render_pass::SystemStage fn(ResMut<'a, AsyncQueues>)>` +``` + +The implementation should only need to carry over this information and set the type correspondingly +instead of emitting an error. + + +# Drawbacks +[drawbacks]: #drawbacks + +- Potential Loss of Clarity: In some cases, omitting the type might make the code less clear, + especially to newcomers or in codebases where explicit types are part of the documentation style. + It is my belief that this is a choice better left for the developers as in the case of `let` bindings. +- Anything else? + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +- Impact of Not Doing This: Rust code remains more verbose than necessary, especially in complex scenarios, and macro authors face challenges with type specifications. + + +# Prior art +[prior-art]: #prior-art + +In [RFC#1623](https://github.com/rust-lang/rfcs/pull/1623) we added `'static` lifetimes to every reference or generics lifetime value in `static` or `const` declarations. + + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + +Should we allow assignment of unnameable types? For example, +```rs +let A = |a: u32| { + 123_i32 +}; + +``` + +``` +error: missing type for `const` item + | +28 | const A = |a: u32| { + | ^ + | +note: however, the inferred type `[closure@render_pass.rs:28:11]` cannot be named + | +28 | const A = |a: u32| { + | ___________^ +29 | | 1_i32 +30 | | }; + | |_^ +``` + +If this significantly complicates the implementation, we can leave it outside the scope of thie RFC. From af46bdb265c384faea08fbdf53b612d7b7250980 Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Thu, 21 Dec 2023 04:19:47 -0800 Subject: [PATCH 02/19] Updates --- text/0000-const-type-inference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index f0a601750a7..fe8127f8db8 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -1,6 +1,6 @@ - Feature Name: const_type_inference - Start Date: 2023-12-21 -- RFC PR: [rust-lang/rfcs#0000](https://github.com/rust-lang/rfcs/pull/0000) +- RFC PR: [rust-lang/rfcs#3546](https://github.com/rust-lang/rfcs/pull/3546) - Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) # Summary From 7c3a9639e4dcff3cfa44f798adc0331ab62f7b47 Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Thu, 21 Dec 2023 04:25:19 -0800 Subject: [PATCH 03/19] updates --- text/0000-const-type-inference.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index fe8127f8db8..cbea86e3ea1 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -22,6 +22,10 @@ the type of the const seems trivial. However, this isn't always the case: - When creating macros, the precise type might not be known to the macro author. - Code generators may not have enough information to easily determine the type. +This change aims to make Rust code more concise and maintainable, especially in scenarios where the types of +const items are complicated or not easily expressible. + + # Guide-level explanation [guide-level-explanation]: #guide-level-explanation @@ -34,10 +38,6 @@ static MESSAGE = "Hello, World!"; // inferred as &'static str const FN_PTR = std::string::String::default; // inferred as fn() -> String ``` -This change aims to make Rust code more concise and maintainable, especially in scenarios where the types of -const items are complicated or not easily expressible. - - # Reference-level explanation [reference-level-explanation]: #reference-level-explanation @@ -77,7 +77,7 @@ instead of emitting an error. [rationale-and-alternatives]: #rationale-and-alternatives - Impact of Not Doing This: Rust code remains more verbose than necessary, especially in complex scenarios, and macro authors face challenges with type specifications. - +- Alternative: Allowing the naming of function types as in [#3476](https://github.com/rust-lang/rfcs/pull/3476) may help resolve some of the cases where type inference is needed. # Prior art [prior-art]: #prior-art From 59a402c38cd8b428bbc9c36b6f4b210526012815 Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Thu, 21 Dec 2023 04:28:30 -0800 Subject: [PATCH 04/19] typo --- text/0000-const-type-inference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index cbea86e3ea1..bb23e000c3a 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -111,4 +111,4 @@ note: however, the inferred type `[closure@render_pass.rs:28:11]` cannot be name | |_^ ``` -If this significantly complicates the implementation, we can leave it outside the scope of thie RFC. +If this significantly complicates the implementation, we can leave it outside the scope of this RFC. From ad81b61178f04c00b4bc45255bd26a69e3ff126a Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Thu, 21 Dec 2023 10:04:23 -0800 Subject: [PATCH 05/19] Update text/0000-const-type-inference.md Co-authored-by: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> --- text/0000-const-type-inference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index bb23e000c3a..343f74a5d21 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -90,7 +90,7 @@ In [RFC#1623](https://github.com/rust-lang/rfcs/pull/1623) we added `'static` li Should we allow assignment of unnameable types? For example, ```rs -let A = |a: u32| { +const A = |a: u32| { 123_i32 }; From a9b7cf9acc19091129068fdb6adbd12a6d0c9c7e Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Fri, 22 Dec 2023 03:01:22 -0800 Subject: [PATCH 06/19] Update text/0000-const-type-inference.md Co-authored-by: teor --- text/0000-const-type-inference.md | 1 + 1 file changed, 1 insertion(+) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index 343f74a5d21..854a91c6801 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -71,6 +71,7 @@ instead of emitting an error. - Potential Loss of Clarity: In some cases, omitting the type might make the code less clear, especially to newcomers or in codebases where explicit types are part of the documentation style. It is my belief that this is a choice better left for the developers as in the case of `let` bindings. + This drawback could be addressed using an allow-by-default clippy lint for `const` and `static` types. - Anything else? # Rationale and alternatives From 9ec41980b5d87eb93cc5c54ff6232bbb0b3370ff Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Fri, 22 Dec 2023 03:02:02 -0800 Subject: [PATCH 07/19] Update text/0000-const-type-inference.md Co-authored-by: teor --- text/0000-const-type-inference.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index 854a91c6801..bf3bad74f64 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -25,6 +25,11 @@ the type of the const seems trivial. However, this isn't always the case: This change aims to make Rust code more concise and maintainable, especially in scenarios where the types of const items are complicated or not easily expressible. +Inferring constant types also improves the ergonomics of the language, particularly for new users. Some users are +coming from languages where most (or all) types are inferred. So inferring obvious types matches their +expectations. Other new users are focused on learning ownership, or other core Rust concepts. Reducing the +amount of boilerplate reduces their mental load. This reduction in mental load also helps experienced programmers. + # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From 76d608655b7441681a5b520eff8358bad5c39193 Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Fri, 22 Dec 2023 03:28:12 -0800 Subject: [PATCH 08/19] Updates --- text/0000-const-type-inference.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index bf3bad74f64..03eba7f395c 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -12,6 +12,7 @@ Allow type inference for `const` or `static` when the type of the initial value [motivation]: #motivation Rust currently requires explicit type annotations for `const` and `static` items. +It was decided that all public API points should be "obviously semver stable" rather than "quick to type". In simple cases, explicitly writing out @@ -74,10 +75,13 @@ instead of emitting an error. [drawbacks]: #drawbacks - Potential Loss of Clarity: In some cases, omitting the type might make the code less clear, - especially to newcomers or in codebases where explicit types are part of the documentation style. + especially to newcomers or when explicit types are needed to understanding the purpose of the item. It is my belief that this is a choice better left for the developers as in the case of `let` bindings. - This drawback could be addressed using an allow-by-default clippy lint for `const` and `static` types. -- Anything else? +- Semvar compatibilty: It's a good idea that public API endpoints should be "obviously semvar stable". + However, not all `const` or `static` items are public, and explicit typing isn't always important for semvar stability. + Requiring explicit typing for this reason seems a bit heavy handed. + +Both of these drawback could be addressed using an allow-by-default clippy lint for `const` and `static` types. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives From 72dd92ab5ddbd478ecd4a43c45acd609f4949423 Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Fri, 22 Dec 2023 03:37:39 -0800 Subject: [PATCH 09/19] typo --- text/0000-const-type-inference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index 03eba7f395c..d2e2960f3ae 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -27,7 +27,7 @@ This change aims to make Rust code more concise and maintainable, especially in const items are complicated or not easily expressible. Inferring constant types also improves the ergonomics of the language, particularly for new users. Some users are -coming from languages where most (or all) types are inferred. So inferring obvious types matches their +coming from languages where most (or all) types are inferred, so inferring obvious types matches their expectations. Other new users are focused on learning ownership, or other core Rust concepts. Reducing the amount of boilerplate reduces their mental load. This reduction in mental load also helps experienced programmers. From 1ba81dbe66432072e750c4181a694eae1ff75d5d Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Fri, 22 Dec 2023 10:52:23 -0800 Subject: [PATCH 10/19] Update text/0000-const-type-inference.md Co-authored-by: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> --- text/0000-const-type-inference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index d2e2960f3ae..1b9a599edf9 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -77,7 +77,7 @@ instead of emitting an error. - Potential Loss of Clarity: In some cases, omitting the type might make the code less clear, especially to newcomers or when explicit types are needed to understanding the purpose of the item. It is my belief that this is a choice better left for the developers as in the case of `let` bindings. -- Semvar compatibilty: It's a good idea that public API endpoints should be "obviously semvar stable". +- Semver compatibilty: The API surface of the type is implicit, changing the right-hand side in subtle ways can change the type in a way that can be hard to notice, for example between different integer types. However, not all `const` or `static` items are public, and explicit typing isn't always important for semvar stability. Requiring explicit typing for this reason seems a bit heavy handed. From a0e2a390a9b46bfb3fc19e7b814aedf506b1192b Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Fri, 22 Dec 2023 10:53:14 -0800 Subject: [PATCH 11/19] Update text/0000-const-type-inference.md Co-authored-by: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> --- text/0000-const-type-inference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index 1b9a599edf9..18663ecc989 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -78,7 +78,7 @@ instead of emitting an error. especially to newcomers or when explicit types are needed to understanding the purpose of the item. It is my belief that this is a choice better left for the developers as in the case of `let` bindings. - Semver compatibilty: The API surface of the type is implicit, changing the right-hand side in subtle ways can change the type in a way that can be hard to notice, for example between different integer types. - However, not all `const` or `static` items are public, and explicit typing isn't always important for semvar stability. + However, not all `const` or `static` items are public, and in many cases the type is obvious enough that semver isn't a concern. Requiring explicit typing for this reason seems a bit heavy handed. Both of these drawback could be addressed using an allow-by-default clippy lint for `const` and `static` types. From ce637789218876f8ef433cd2464d923d255896c9 Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Tue, 9 Jan 2024 13:16:13 -0800 Subject: [PATCH 12/19] updates --- text/0000-const-type-inference.md | 62 +++++++++++++++---------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index 18663ecc989..060caeab2a1 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -36,12 +36,23 @@ amount of boilerplate reduces their mental load. This reduction in mental load a [guide-level-explanation]: #guide-level-explanation You may declare constants and static variables without specifying their types when the type can be inferred -from the initial value. For example: +from the initial value, subjecting to the following constraints: +- All numerical types contributing to the inference must be specified. +- The type must be partially specified. At the very least, a `_` placeholder must be used, but the `_` placeholder + may also appear anywhere in a nested type. + +For example: ```rs -const PI = 3.1415; // inferred as f64 -static MESSAGE = "Hello, World!"; // inferred as &'static str -const FN_PTR = std::string::String::default; // inferred as fn() -> String +const NO = false; // missing type for `const` item; hint: provide a type or add `_` placeholder +const PI: _ = 3.1415; // Ambiguous numeric type +const PI: _ = 3.1415_f32; // Ok +const WRAPPED_PI: MyStruct<_> = MyStruct(3.1415_f32); // Ok + + +static MESSAGE: _ = "Hello, World!"; // inferred as &'static str +static ARR: [u8; _] = [12, 23, 34, 45]; // inferred as [u8; 4] +const FN_PTR: _ = std::string::String::default; // inferred as fn() -> String ``` # Reference-level explanation @@ -78,16 +89,24 @@ instead of emitting an error. especially to newcomers or when explicit types are needed to understanding the purpose of the item. It is my belief that this is a choice better left for the developers as in the case of `let` bindings. - Semver compatibilty: The API surface of the type is implicit, changing the right-hand side in subtle ways can change the type in a way that can be hard to notice, for example between different integer types. - However, not all `const` or `static` items are public, and in many cases the type is obvious enough that semver isn't a concern. - Requiring explicit typing for this reason seems a bit heavy handed. + However, not all `const` or `static` items are public, and in many cases the type is obvious enough that semver isn't a concern. Requiring explicit typing for this reason seems a bit heavy handed. -Both of these drawback could be addressed using an allow-by-default clippy lint for `const` and `static` types. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -- Impact of Not Doing This: Rust code remains more verbose than necessary, especially in complex scenarios, and macro authors face challenges with type specifications. -- Alternative: Allowing the naming of function types as in [#3476](https://github.com/rust-lang/rfcs/pull/3476) may help resolve some of the cases where type inference is needed. +## Impact of Not Doing This: + +Rust code remains more verbose than necessary, especially in complex scenarios, and macro authors face challenges with type specifications. + +## Alternatives + +Allowing the naming of function types as in [#3476](https://github.com/rust-lang/rfcs/pull/3476) may help resolve some of the cases where type inference is needed. + +`type_alias_impl_trait` may also partially address the problem. In particular, it helps with unnameable types +and macro / code generator output, without the drawbacks of loss of clarity and semvar trouble. +However, there might be cases where we do not want the type to be hidden behind an `impl Trait`. +This also won't help with array lengths or types that do not implement a particular trait. # Prior art [prior-art]: #prior-art @@ -98,27 +117,4 @@ In [RFC#1623](https://github.com/rust-lang/rfcs/pull/1623) we added `'static` li # Unresolved questions [unresolved-questions]: #unresolved-questions -Should we allow assignment of unnameable types? For example, -```rs -const A = |a: u32| { - 123_i32 -}; - -``` - -``` -error: missing type for `const` item - | -28 | const A = |a: u32| { - | ^ - | -note: however, the inferred type `[closure@render_pass.rs:28:11]` cannot be named - | -28 | const A = |a: u32| { - | ___________^ -29 | | 1_i32 -30 | | }; - | |_^ -``` - -If this significantly complicates the implementation, we can leave it outside the scope of this RFC. +None. From 2642e55d5b1ce38407cb129de25a3c103e8b8d17 Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Wed, 10 Jan 2024 01:20:09 -0800 Subject: [PATCH 13/19] update --- text/0000-const-type-inference.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index 060caeab2a1..dac0c7c7cc5 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -37,8 +37,9 @@ amount of boilerplate reduces their mental load. This reduction in mental load a You may declare constants and static variables without specifying their types when the type can be inferred from the initial value, subjecting to the following constraints: -- All numerical types contributing to the inference must be specified. -- The type must be partially specified. At the very least, a `_` placeholder must be used, but the `_` placeholder +- The types of all literals must be fully constrained, which generally means numeric literals must either + have a type suffix, or the type must specify their type +- The typing may not be entirely omitted. At the very least, a `_` placeholder must be used, but the `_` placeholder may also appear anywhere in a nested type. For example: From dfc772760699db3f63b3e9bcda4bcfc9856530dd Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Wed, 17 Jan 2024 22:53:00 -0800 Subject: [PATCH 14/19] Address #2010 --- text/0000-const-type-inference.md | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index dac0c7c7cc5..963c4fb2c0a 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -106,14 +106,39 @@ Allowing the naming of function types as in [#3476](https://github.com/rust-lang `type_alias_impl_trait` may also partially address the problem. In particular, it helps with unnameable types and macro / code generator output, without the drawbacks of loss of clarity and semvar trouble. -However, there might be cases where we do not want the type to be hidden behind an `impl Trait`. -This also won't help with array lengths or types that do not implement a particular trait. +However, it cannot fully replace inference because +- There are cases where we do not want the type to be hidden behind an `impl Trait`. +- Defining a trait for the const item might be difficult - for example, when the + const item is a function pointer with a variable number of arguments. +- The const item might be generated from a macro, and the macro might not + want to require a separate trait to be defined. +- This also won't help with array lengths or types that do not implement a particular trait. # Prior art [prior-art]: #prior-art In [RFC#1623](https://github.com/rust-lang/rfcs/pull/1623) we added `'static` lifetimes to every reference or generics lifetime value in `static` or `const` declarations. +In [RFC#2010](https://github.com/rust-lang/rfcs/pull/2010) const/static type inference +was proposed, but the RFC was **postponed**. The [reason](https://github.com/rust-lang/rfcs/pull/2010#issuecomment-325827854) can be summarized as follows: + +- Things we can do with const/static was quite limited at the time. +Const/static type inference fails to provide value for simple cases such as `const SOMETHING = 32;` +- The team wanted to move forward in other areas (e.g. impl Trait) before moving on to solve this problem. + +However, after 7 years it is now time to revisit this topic: + +- The things that can be done in a const expression has been greatly expanded since + 2017, which means that it is more likely to use a complicated type or an unnameable type on const/static items. +- It is now possible to use impl types in function returns (although having impl type aliases could provide a similar solution for const and statics) +- Const and static aren't necessarily at the top level. It feels weird that the type can be elided on a let statement inside a function, but not on a const or static inside a function +- The original RFC resolution of **postpone** was made at least partially based on + statistics done by @schuster. This RFC was proposed with the motivation of enabling the use + of unnameable types in const/static items. Because this RFC enables new behaviors, + data on current usage isn't very useful for determining how much it might improve language + expressiveness. + + # Unresolved questions [unresolved-questions]: #unresolved-questions From f7ee9b4aa9985c93ee3a9807f10819dee687edd2 Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Wed, 17 Jan 2024 23:00:53 -0800 Subject: [PATCH 15/19] Updates --- text/0000-const-type-inference.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index 963c4fb2c0a..8bff25f4717 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -12,7 +12,8 @@ Allow type inference for `const` or `static` when the type of the initial value [motivation]: #motivation Rust currently requires explicit type annotations for `const` and `static` items. -It was decided that all public API points should be "obviously semver stable" rather than "quick to type". +It was decided that all public API and top level items must be "obviously semver stable" rather than "quick to type". However, this philosophy needs to be carefully weighted against +language expressiveness. In simple cases, explicitly writing out @@ -23,13 +24,10 @@ the type of the const seems trivial. However, this isn't always the case: - When creating macros, the precise type might not be known to the macro author. - Code generators may not have enough information to easily determine the type. -This change aims to make Rust code more concise and maintainable, especially in scenarios where the types of +This change aims to make Rust more expressive, concise and maintainable, especially in scenarios where the types of const items are complicated or not easily expressible. -Inferring constant types also improves the ergonomics of the language, particularly for new users. Some users are -coming from languages where most (or all) types are inferred, so inferring obvious types matches their -expectations. Other new users are focused on learning ownership, or other core Rust concepts. Reducing the -amount of boilerplate reduces their mental load. This reduction in mental load also helps experienced programmers. +Inferring constant types also improves the ergonomics and consistency of the language, particularly for new users. Types are already being inferred for `let` items, and not allowing inference for obvious types creates a mismatch of expectations, especially considering that const/static items may also be defined inside a function and directly next to a `let` binding. # Guide-level explanation @@ -56,6 +54,8 @@ static ARR: [u8; _] = [12, 23, 34, 45]; // inferred as [u8; 4] const FN_PTR: _ = std::string::String::default; // inferred as fn() -> String ``` +In summary, globals should have sandboxed inference context, where their type would be fully known after all constraints in const expr block has been applied; i.e. no default types for literals, nor implicit casts should be allowed. + # Reference-level explanation [reference-level-explanation]: #reference-level-explanation From 6104660dbcc5cd7387b0f74d0d561bf47fe3535e Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Wed, 17 Jan 2024 23:14:28 -0800 Subject: [PATCH 16/19] update --- text/0000-const-type-inference.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index 8bff25f4717..12c3d110264 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -27,7 +27,7 @@ the type of the const seems trivial. However, this isn't always the case: This change aims to make Rust more expressive, concise and maintainable, especially in scenarios where the types of const items are complicated or not easily expressible. -Inferring constant types also improves the ergonomics and consistency of the language, particularly for new users. Types are already being inferred for `let` items, and not allowing inference for obvious types creates a mismatch of expectations, especially considering that const/static items may also be defined inside a function and directly next to a `let` binding. +Inferring constant types also improves the ergonomics and consistency of the language, particularly for new users. Types are already being inferred for `let` bindings, and not allowing inference for obvious `const` or `static` items creates a mismatch of expectations, especially when `const`/`static` items may be defined inside a function, directly next to a `let` binding. # Guide-level explanation From 858a178d2c3f7b6a199ea5f855853b4a45abcafc Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Wed, 17 Jan 2024 23:24:35 -0800 Subject: [PATCH 17/19] Update --- text/0000-const-type-inference.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index 12c3d110264..6b1f2dd6cd5 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -12,8 +12,7 @@ Allow type inference for `const` or `static` when the type of the initial value [motivation]: #motivation Rust currently requires explicit type annotations for `const` and `static` items. -It was decided that all public API and top level items must be "obviously semver stable" rather than "quick to type". However, this philosophy needs to be carefully weighted against -language expressiveness. +It was decided that all public API and top level items must be "obviously semver stable" rather than "quick to type". In simple cases, explicitly writing out @@ -37,7 +36,7 @@ You may declare constants and static variables without specifying their types wh from the initial value, subjecting to the following constraints: - The types of all literals must be fully constrained, which generally means numeric literals must either have a type suffix, or the type must specify their type -- The typing may not be entirely omitted. At the very least, a `_` placeholder must be used, but the `_` placeholder +- When declaring a top-level item, the typing may not be entirely omitted. At the very least, a `_` placeholder must be used, but the `_` placeholder may also appear anywhere in a nested type. For example: @@ -51,7 +50,7 @@ const WRAPPED_PI: MyStruct<_> = MyStruct(3.1415_f32); // Ok static MESSAGE: _ = "Hello, World!"; // inferred as &'static str static ARR: [u8; _] = [12, 23, 34, 45]; // inferred as [u8; 4] -const FN_PTR: _ = std::string::String::default; // inferred as fn() -> String +const FN: _ = std::string::String::default; // inferred as the unnameable type of ZST closure asociated with this item. Its type is reported by `type_name_of_val` as ::std::string::String::default ``` In summary, globals should have sandboxed inference context, where their type would be fully known after all constraints in const expr block has been applied; i.e. no default types for literals, nor implicit casts should be allowed. @@ -89,8 +88,15 @@ instead of emitting an error. - Potential Loss of Clarity: In some cases, omitting the type might make the code less clear, especially to newcomers or when explicit types are needed to understanding the purpose of the item. It is my belief that this is a choice better left for the developers as in the case of `let` bindings. -- Semver compatibilty: The API surface of the type is implicit, changing the right-hand side in subtle ways can change the type in a way that can be hard to notice, for example between different integer types. - However, not all `const` or `static` items are public, and in many cases the type is obvious enough that semver isn't a concern. Requiring explicit typing for this reason seems a bit heavy handed. +- Semver compatibilty: The API surface of the type is implicit, changing the right-hand side in subtle ways can change the type in a way that can be hard to notice, for example between different integer types. This goes against the rule that "all top-level items must be fully type-annotated". + +However, this philosophy needs to be carefully weighted against +language expressiveness and usability. + +Not all `const` or `static` items are public, and in many cases the type is obvious enough that semver isn't a concern. Requiring explicit typing for this reason seems a bit heavy handed. + +It is for this reason that we require "opt-in" for all places where type inference is desired +by requiring at least a "_" placeholder for top level items. # Rationale and alternatives From 007e0cbb1ff10aa24d5dcb93de678155aff45fad Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Wed, 17 Jan 2024 23:32:01 -0800 Subject: [PATCH 18/19] address clippy lint --- text/0000-const-type-inference.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index 6b1f2dd6cd5..811174cdd3c 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -95,8 +95,9 @@ language expressiveness and usability. Not all `const` or `static` items are public, and in many cases the type is obvious enough that semver isn't a concern. Requiring explicit typing for this reason seems a bit heavy handed. -It is for this reason that we require "opt-in" for all places where type inference is desired -by requiring at least a "_" placeholder for top level items. +It is for this reason that we require "opt-in" where type inference is desired +by requiring at least a "_" placeholder for top level items. A clippy lint will also be added +when such top level item may in fact be named. # Rationale and alternatives From bf115d26c5dd5a39c4b02470f7343a136057eb56 Mon Sep 17 00:00:00 2001 From: Zhixing Zhang Date: Wed, 17 Jan 2024 23:33:27 -0800 Subject: [PATCH 19/19] typo --- text/0000-const-type-inference.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/text/0000-const-type-inference.md b/text/0000-const-type-inference.md index 811174cdd3c..f93570aedf7 100644 --- a/text/0000-const-type-inference.md +++ b/text/0000-const-type-inference.md @@ -19,7 +19,7 @@ In simple cases, explicitly writing out the type of the const seems trivial. However, this isn't always the case: - Sometimes the constant's value is complex, making the explicit type overly verbose. -- In some cases, the type may be unnameable. +- In some cases, the type may be unnamable. - When creating macros, the precise type might not be known to the macro author. - Code generators may not have enough information to easily determine the type. @@ -50,7 +50,7 @@ const WRAPPED_PI: MyStruct<_> = MyStruct(3.1415_f32); // Ok static MESSAGE: _ = "Hello, World!"; // inferred as &'static str static ARR: [u8; _] = [12, 23, 34, 45]; // inferred as [u8; 4] -const FN: _ = std::string::String::default; // inferred as the unnameable type of ZST closure asociated with this item. Its type is reported by `type_name_of_val` as ::std::string::String::default +const FN: _ = std::string::String::default; // inferred as the unnamable type of ZST closure associated with this item. Its type is reported by `type_name_of_val` as ::std::string::String::default ``` In summary, globals should have sandboxed inference context, where their type would be fully known after all constraints in const expr block has been applied; i.e. no default types for literals, nor implicit casts should be allowed. @@ -111,7 +111,7 @@ Rust code remains more verbose than necessary, especially in complex scenarios, Allowing the naming of function types as in [#3476](https://github.com/rust-lang/rfcs/pull/3476) may help resolve some of the cases where type inference is needed. -`type_alias_impl_trait` may also partially address the problem. In particular, it helps with unnameable types +`type_alias_impl_trait` may also partially address the problem. In particular, it helps with unnamable types and macro / code generator output, without the drawbacks of loss of clarity and semvar trouble. However, it cannot fully replace inference because - There are cases where we do not want the type to be hidden behind an `impl Trait`. @@ -136,12 +136,12 @@ Const/static type inference fails to provide value for simple cases such as `con However, after 7 years it is now time to revisit this topic: - The things that can be done in a const expression has been greatly expanded since - 2017, which means that it is more likely to use a complicated type or an unnameable type on const/static items. + 2017, which means that it is more likely to use a complicated type or an unnamable type on const/static items. - It is now possible to use impl types in function returns (although having impl type aliases could provide a similar solution for const and statics) - Const and static aren't necessarily at the top level. It feels weird that the type can be elided on a let statement inside a function, but not on a const or static inside a function - The original RFC resolution of **postpone** was made at least partially based on statistics done by @schuster. This RFC was proposed with the motivation of enabling the use - of unnameable types in const/static items. Because this RFC enables new behaviors, + of unnamable types in const/static items. Because this RFC enables new behaviors, data on current usage isn't very useful for determining how much it might improve language expressiveness.