From 10584e30a942eb2613deea7024eaf5eacf23fc95 Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Wed, 7 Jun 2023 13:00:24 +0800 Subject: [PATCH 01/14] Infered enums --- text/0000-infered-enums.md | 174 +++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 text/0000-infered-enums.md diff --git a/text/0000-infered-enums.md b/text/0000-infered-enums.md new file mode 100644 index 00000000000..d6f6f1c2633 --- /dev/null +++ b/text/0000-infered-enums.md @@ -0,0 +1,174 @@ +- Feature Name: Inferred Types +- Start Date: 2023-06-06 +- 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 introduces a feature allowing the base type of enums and structs to be inferred in certain contexts including but not limited to function calls. The syntax is as follows `_::Variant`. + + +# Motivation +[motivation]: #motivation + +Using large libraries usually requires developers to import large amounts of traits, structures, and enums. To just call a function on an implementation can take upwards of 3 imports. One way developers have solved this is by importing everything from specific modules. Importing everything has its own problems like trying to guess where imports are actually from. Developers need a low compromise solution to solve this problem and that comes in the form of inferred types. + +Swift has had a system to infer types since around 2014. Its system has been praised by many developers around the world. It’s system is as follows: +```swift +enum EnumExample { + case variant + case variant2 +} + + +example(data: .variant); +``` + +This RFC’s intent was to create something similar to the system already tried and tested in swift. Additionally, the underscore is already used to imply type’s lifetimes so it runs consistent with the rust theme. + + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + + +Implied types basically replace the path to a type with `_`. They can be used in places where strong typing exists. This includes function calls and function returns. You can think about the `_` as expanding into the type’s name. + +Function calls (struct): +```rust +fn my_function(data: MyStruct) { /* ... */ } + +// my_function(MyStruct { +// value: 1 +// }); +my_function(_ { + value: 1 +}); +``` + +Function calls (impl): +```rust +fn my_function(data: MyStruct) { /* ... */ } + +// my_function(MyStruct::new()}); +my_function(_::new()); +``` + +Function returns (enum): +```rust +fn my_function() -> MyEnum { + // MyEnum::MyVarient + _::MyVarient +} +``` + +Match arms: +```rust +match Example::One { + _::One => println!("One!"), + _::Two => println!("Two!") +}; +``` + +It is important to note that `_` only represents the type; if you have (for example) another enum that can be coerced into the type, you will need to specify it manually. Additionally, any traits required to call an impl will still have to be imported. + +```rust +fn my_function(data: MyStruct) { /* ... */ } + + +my_function(MyStruct2::do_something().into()); // ✅ + + +my_function(_::do_something().into()); // ❌ variant or associated item not found in `MyStruct` +``` + +When developing, an IDE will display the type’s name next to the underscore as an IDE hint similar to implied types. Reading and maintaining code should not be impacted because the function name should give context to the type. + + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +This RFC should not take much to implement as you can think of the `_` as if it was just expanding to a type based on the context and where it’s used. Anywhere with a strongly typed argument as mentioned above can be inferred. This additionally includes let and const statements with type definitions in the left hand side. + +Here is an example of what should happen when compiling. +```rust +let var: MyEnum = _::Variant; +``` +becomes: +```rust +let var = MyEnum::Variant; +``` + +One issue is getting the type of a given context mentioned in [rust-lang/rust#8995](https://github.com/rust-lang/rust/issues/8995). + +Finally, here are some examples of non-strict typings that can not be allowed. +```rust +fn convert(argument: T) -> Example {/* ... */} + +do_something(convert(_::new())) +// ^^^^^^^^ Cannot infer type on generic type argument +``` + +However, ones where a generic argument can collapse into strict typing can be allowed. The below works because `T` becomes `Example`. This wouldn’t work if `do_something`, however, took a generic. +```rust +fn do_something(argument: Example) {/* ... */} +fn convert(argument: T) -> T {/* ... */} + +do_something(convert(_::new())) +``` + + +# Drawbacks +[drawbacks]: #drawbacks + +In the thread [[IDEA] Implied enum types](https://internals.rust-lang.org/t/idea-implied-enum-types/18349), many people had a few concerns about this feature. + +These RFCs could create bugs. An example of this is if a function changes has two enum parameters that share common variant names. Because it’s implied, it would still compile with this bug causing UB. +```rust +enum RadioState { + Disabled, + Enabled, +} + +enum WifiConfig { + Disabled, + Reverse, + Enabled, +} + +fn configure_wireless(radio: RadioState, wifi: WifiConfig) { /* ... */ } +``` + +Another issue with this is that the syntax `_::` could be mistaken for `::` meaning crate. + + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + + +Maintainers should accept this proposal because it can simplify writing Rust code. Especially in enum heavy libraries like [windows-rs](https://github.com/microsoft/windows-rs). There have been many ideas for what this operator should be including `::` add `.`. Despite that, the underscore is the best because it has already been used to infer lifetimes. Additionally the underscore by itself can be used to construct a struct creating a consistent experience. + + +If this RFC doesn’t happen, writing rust code will continue to feel bloated and old. + + +# Prior art +[prior-art]: #prior-art + + +Apple’s Swift had enum inference since 2014 and is not used in most swift codebases with no issues. One thing people have noticed, though, is that it could be used for so much more! That was quite limited and in creating a rust implementation, people need to extend what swift pioneered and make it more universal. That is why this RFC proposes to make the underscore a general operator that can be used outside the small use case of enums and allow it to be used in structs. + + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + + +A few kinks on this are whether it should be required to have the type in scope. Lots of people could point to traits and say that they should but others would disagree. From an individual standpoint, I don’t think it should require any imports but, it really depends on the implementers as personally, I am not an expert in *this* subject. + + +# Future possibilities +[future-possibilities]: #future-possibilities + + +I can’t think of anything \ No newline at end of file From cc80833494363f60e640f2790a4fad830346b439 Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Wed, 7 Jun 2023 13:03:49 +0800 Subject: [PATCH 02/14] Change file name --- text/{0000-infered-enums.md => 0000-infered-types.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename text/{0000-infered-enums.md => 0000-infered-types.md} (100%) diff --git a/text/0000-infered-enums.md b/text/0000-infered-types.md similarity index 100% rename from text/0000-infered-enums.md rename to text/0000-infered-types.md From bbfe24943f3fd5eeab4e768833ab84eff738bb0c Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Sun, 11 Jun 2023 21:32:27 -0700 Subject: [PATCH 03/14] Make it better --- text/0000-infered-types.md | 67 ++++++++++++-------------------------- 1 file changed, 21 insertions(+), 46 deletions(-) diff --git a/text/0000-infered-types.md b/text/0000-infered-types.md index d6f6f1c2633..5cd4560dc48 100644 --- a/text/0000-infered-types.md +++ b/text/0000-infered-types.md @@ -7,35 +7,20 @@ # Summary [summary]: #summary -This RFC introduces a feature allowing the base type of enums and structs to be inferred in certain contexts including but not limited to function calls. The syntax is as follows `_::Variant`. +This RFC introduces a feature allowing the base type of enums and structs to be inferred in contexts where strict typing information can be exist. Some examples of strict typing include match statements and function calls. The syntax is `_::EnumVariant` for enums, `_ { a: 1 }` for constructing structs, and `_::method()` for impls and traits wher `_` is the type. # Motivation [motivation]: #motivation -Using large libraries usually requires developers to import large amounts of traits, structures, and enums. To just call a function on an implementation can take upwards of 3 imports. One way developers have solved this is by importing everything from specific modules. Importing everything has its own problems like trying to guess where imports are actually from. Developers need a low compromise solution to solve this problem and that comes in the form of inferred types. - -Swift has had a system to infer types since around 2014. Its system has been praised by many developers around the world. It’s system is as follows: -```swift -enum EnumExample { - case variant - case variant2 -} - - -example(data: .variant); -``` - -This RFC’s intent was to create something similar to the system already tried and tested in swift. Additionally, the underscore is already used to imply type’s lifetimes so it runs consistent with the rust theme. - +Rust's goals include clean syntax, that comes with a consice syntax and features like macros making it easier to not repeat yourself. Having to write a type every time you want to do something can be very annoying, repetetive, and not to mention messy. This is a huge problem especialy in large projects with heavy dependency on enums. Additionaly, with large libraries developers can expect to import upwords from 3 traits, structures, and enums. One way developers have solved this is by importing everything from specific modules like [`windows-rs`](https://github.com/microsoft/windows-rs). This is problematic because at a glance, it can not be determined where a module comes from. It can be said that developers need a low compromise solution to solve the problem of large imports and messy code. The intent of this RFC’s is to create something that developer friendly yet still conforming to all of rust's goals. # Guide-level explanation [guide-level-explanation]: #guide-level-explanation +When crating a struct or enum, infered types can simplify the type into just a underscore. It is important to note, however, that they do not work when the type is not specific enough to be infered like: type parameters. Below are some examples where they do and don't work. -Implied types basically replace the path to a type with `_`. They can be used in places where strong typing exists. This includes function calls and function returns. You can think about the `_` as expanding into the type’s name. - -Function calls (struct): +Function calls (structs): ```rust fn my_function(data: MyStruct) { /* ... */ } @@ -65,10 +50,17 @@ fn my_function() -> MyEnum { Match arms: ```rust -match Example::One { - _::One => println!("One!"), - _::Two => println!("Two!") -}; +enum Example { + One, + Two +} + +fn my_fn(my_enum: Example) -> String { + match my_enum { + _::One => "One!", + _::Two => "Two!" + } +} ``` It is important to note that `_` only represents the type; if you have (for example) another enum that can be coerced into the type, you will need to specify it manually. Additionally, any traits required to call an impl will still have to be imported. @@ -80,27 +72,16 @@ fn my_function(data: MyStruct) { /* ... */ } my_function(MyStruct2::do_something().into()); // ✅ -my_function(_::do_something().into()); // ❌ variant or associated item not found in `MyStruct` +my_function(_::do_something().into()); // ❌ error[E0599]: variant or associated item not found in `MyStruct` ``` -When developing, an IDE will display the type’s name next to the underscore as an IDE hint similar to implied types. Reading and maintaining code should not be impacted because the function name should give context to the type. - # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -This RFC should not take much to implement as you can think of the `_` as if it was just expanding to a type based on the context and where it’s used. Anywhere with a strongly typed argument as mentioned above can be inferred. This additionally includes let and const statements with type definitions in the left hand side. - -Here is an example of what should happen when compiling. -```rust -let var: MyEnum = _::Variant; -``` -becomes: -```rust -let var = MyEnum::Variant; -``` +The underscore operator infers the type of a stuct or enum where there is enough type information in a context in order to infer an exact type. In a statement like `_::Variant`, you can imagine the underscore to be the type. That means that anything you could do with an actual type like `MyEnum::Variant` will still apply in an infered enum. Ultimately, the enum or struct doesn't need to be imported but, traits and other specfied things will need to be imported. -One issue is getting the type of a given context mentioned in [rust-lang/rust#8995](https://github.com/rust-lang/rust/issues/8995). +Due to how the rust compiler currently works, lots of changes will need to be made to allow paths to be infered in an order that allows for all of the mentioned. One issue is getting the type of a given context mentioned in [rust-lang/rust#8995](https://github.com/rust-lang/rust/issues/8995). Finally, here are some examples of non-strict typings that can not be allowed. ```rust @@ -124,7 +105,7 @@ do_something(convert(_::new())) In the thread [[IDEA] Implied enum types](https://internals.rust-lang.org/t/idea-implied-enum-types/18349), many people had a few concerns about this feature. -These RFCs could create bugs. An example of this is if a function changes has two enum parameters that share common variant names. Because it’s implied, it would still compile with this bug causing UB. +These RFCs could create bugs. An example of this is if a function changes has two enum parameters that share common variant names. Because it’s implied, it would still compile with this bug createing unintended behavior wharas by specifying the type names, the compiler would thrown an error. ```rust enum RadioState { Disabled, @@ -146,11 +127,7 @@ Another issue with this is that the syntax `_::` could be mistaken for `::` mean # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives - -Maintainers should accept this proposal because it can simplify writing Rust code. Especially in enum heavy libraries like [windows-rs](https://github.com/microsoft/windows-rs). There have been many ideas for what this operator should be including `::` add `.`. Despite that, the underscore is the best because it has already been used to infer lifetimes. Additionally the underscore by itself can be used to construct a struct creating a consistent experience. - - -If this RFC doesn’t happen, writing rust code will continue to feel bloated and old. +There have been many ideas for what this operator should be including `::` add `.`. Despite that, the underscore is the best because it has already been used to infer lifetimes. Additionally the underscore by itself can be used to construct a struct creating a consistent experience. Maintainers should accept this proposal because it can simplify writing Rust code and prevent the large problem of reputition in switch statements. # Prior art @@ -159,13 +136,11 @@ If this RFC doesn’t happen, writing rust code will continue to feel bloated an Apple’s Swift had enum inference since 2014 and is not used in most swift codebases with no issues. One thing people have noticed, though, is that it could be used for so much more! That was quite limited and in creating a rust implementation, people need to extend what swift pioneered and make it more universal. That is why this RFC proposes to make the underscore a general operator that can be used outside the small use case of enums and allow it to be used in structs. - # Unresolved questions [unresolved-questions]: #unresolved-questions -A few kinks on this are whether it should be required to have the type in scope. Lots of people could point to traits and say that they should but others would disagree. From an individual standpoint, I don’t think it should require any imports but, it really depends on the implementers as personally, I am not an expert in *this* subject. - +The implementation of this feature still requires a deep dive into how exactly the compiler should resolve the typings to produce the expected behavior, however, algorithems for finding paths for do an already exist. # Future possibilities [future-possibilities]: #future-possibilities From fb47866de9e01078c5270436a9f4be6c8015abdd Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Tue, 13 Jun 2023 16:31:08 -0700 Subject: [PATCH 04/14] add additional point. --- text/0000-infered-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-infered-types.md b/text/0000-infered-types.md index 5cd4560dc48..54720168122 100644 --- a/text/0000-infered-types.md +++ b/text/0000-infered-types.md @@ -13,7 +13,7 @@ This RFC introduces a feature allowing the base type of enums and structs to be # Motivation [motivation]: #motivation -Rust's goals include clean syntax, that comes with a consice syntax and features like macros making it easier to not repeat yourself. Having to write a type every time you want to do something can be very annoying, repetetive, and not to mention messy. This is a huge problem especialy in large projects with heavy dependency on enums. Additionaly, with large libraries developers can expect to import upwords from 3 traits, structures, and enums. One way developers have solved this is by importing everything from specific modules like [`windows-rs`](https://github.com/microsoft/windows-rs). This is problematic because at a glance, it can not be determined where a module comes from. It can be said that developers need a low compromise solution to solve the problem of large imports and messy code. The intent of this RFC’s is to create something that developer friendly yet still conforming to all of rust's goals. +Rust's goals include clean syntax, that comes with a consice syntax and features like macros making it easier to not repeat yourself. Having to write a type every time you want to do something can be very annoying, repetetive, and not to mention messy. This is a huge problem especialy in large projects with heavy dependency on enums. Additionaly, with large libraries developers can expect to import upwords from 3 traits, structures, and enums. One way developers have solved this is by importing everything from specific modules like [`windows-rs`](https://github.com/microsoft/windows-rs). This is problematic because at a glance, it can not be determined where a module comes from. It can be said that developers need a low compromise solution to solve the problem of large imports and messy code. The intent of this RFC’s is to create something that developer friendly yet still conforming to all of rust's goals. Finally, when using specific rust crates, it can be annoying to have to add one package specificaly for a type definition (like `chrono`). Now it can be prevented! # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From 1e56cefe06b578074a50c968618e320d05f407ed Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Thu, 15 Jun 2023 15:23:20 -0700 Subject: [PATCH 05/14] Fix typo Co-authored-by: teor --- text/0000-infered-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-infered-types.md b/text/0000-infered-types.md index 54720168122..a03afdbf988 100644 --- a/text/0000-infered-types.md +++ b/text/0000-infered-types.md @@ -7,7 +7,7 @@ # Summary [summary]: #summary -This RFC introduces a feature allowing the base type of enums and structs to be inferred in contexts where strict typing information can be exist. Some examples of strict typing include match statements and function calls. The syntax is `_::EnumVariant` for enums, `_ { a: 1 }` for constructing structs, and `_::method()` for impls and traits wher `_` is the type. +This RFC introduces a feature allowing the base type of enums and structs to be inferred in contexts where strict typing information can be exist. Some examples of strict typing include match statements and function calls. The syntax is `_::EnumVariant` for enums, `_ { a: 1 }` for constructing structs, and `_::method()` for impls and traits where `_` is the type. # Motivation From 7934f4ee1ca6e68340a00e7a930f26d6f8385335 Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Thu, 15 Jun 2023 15:24:08 -0700 Subject: [PATCH 06/14] Improve clarity by @teror2345 Co-authored-by: teor --- text/0000-infered-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-infered-types.md b/text/0000-infered-types.md index a03afdbf988..03307daca32 100644 --- a/text/0000-infered-types.md +++ b/text/0000-infered-types.md @@ -13,7 +13,7 @@ This RFC introduces a feature allowing the base type of enums and structs to be # Motivation [motivation]: #motivation -Rust's goals include clean syntax, that comes with a consice syntax and features like macros making it easier to not repeat yourself. Having to write a type every time you want to do something can be very annoying, repetetive, and not to mention messy. This is a huge problem especialy in large projects with heavy dependency on enums. Additionaly, with large libraries developers can expect to import upwords from 3 traits, structures, and enums. One way developers have solved this is by importing everything from specific modules like [`windows-rs`](https://github.com/microsoft/windows-rs). This is problematic because at a glance, it can not be determined where a module comes from. It can be said that developers need a low compromise solution to solve the problem of large imports and messy code. The intent of this RFC’s is to create something that developer friendly yet still conforming to all of rust's goals. Finally, when using specific rust crates, it can be annoying to have to add one package specificaly for a type definition (like `chrono`). Now it can be prevented! +Rust's goals include clean syntax, that comes with a consice syntax and features like macros making it easier to not repeat yourself. Having to write a type every time you want to do something can be very annoying, repetetive, and not to mention messy. This is a huge problem especialy in large projects with heavy dependency on enums. Additionally, with large libraries developers can expect to import many traits, structures, and enums. One way developers have solved this is by importing everything from specific modules like [`windows-rs`](https://github.com/microsoft/windows-rs). This is problematic because at a glance, it can not be determined where a module comes from. It can be said that developers need a low compromise solution to solve the problem of large imports and messy code. The intent of this RFC’s is to create something that developer friendly yet still conforming to all of rust's goals. Finally, when using specific rust crates, it can be annoying to have to add one package specificaly for a type definition (like `chrono`). Now it can be prevented! # Guide-level explanation [guide-level-explanation]: #guide-level-explanation From 060b42104fb7120accd15ef5b1882220138ea62a Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Thu, 15 Jun 2023 15:29:56 -0700 Subject: [PATCH 07/14] Correction by @teor2345 Co-authored-by: teor --- text/0000-infered-types.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/text/0000-infered-types.md b/text/0000-infered-types.md index 03307daca32..0ad7d2e7dac 100644 --- a/text/0000-infered-types.md +++ b/text/0000-infered-types.md @@ -127,7 +127,7 @@ Another issue with this is that the syntax `_::` could be mistaken for `::` mean # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives -There have been many ideas for what this operator should be including `::` add `.`. Despite that, the underscore is the best because it has already been used to infer lifetimes. Additionally the underscore by itself can be used to construct a struct creating a consistent experience. Maintainers should accept this proposal because it can simplify writing Rust code and prevent the large problem of reputition in switch statements. +There have been many ideas for what this operator should be including `::` add `.`. Despite that, the underscore is the best because it has already been used to infer lifetimes and generic types. Additionally the underscore by itself can be used to construct a struct creating a consistent experience. Maintainers should accept this proposal because it can simplify writing Rust code and prevent the large problem of repitition in switch statements. # Prior art From aab1794940c638b71bc84ef24e4722832b20392e Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Thu, 29 Jun 2023 20:07:31 -0700 Subject: [PATCH 08/14] Apply suggestions from code review by: @SOF3 Co-authored-by: Jonathan Chan Kwan Yin Co-authored-by: teor --- text/0000-infered-types.md | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/text/0000-infered-types.md b/text/0000-infered-types.md index 0ad7d2e7dac..0051b6bd424 100644 --- a/text/0000-infered-types.md +++ b/text/0000-infered-types.md @@ -18,7 +18,7 @@ Rust's goals include clean syntax, that comes with a consice syntax and features # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -When crating a struct or enum, infered types can simplify the type into just a underscore. It is important to note, however, that they do not work when the type is not specific enough to be infered like: type parameters. Below are some examples where they do and don't work. +When crating a struct or enum, inferred types can simplify the type into just a underscore. It is important to note, however, that they do not work when the type is not specific enough to be inferred like: type parameters. Below are some examples where they do and don't work. Function calls (structs): ```rust @@ -79,9 +79,8 @@ my_function(_::do_something().into()); // ❌ error[E0599]: variant or associate # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -The underscore operator infers the type of a stuct or enum where there is enough type information in a context in order to infer an exact type. In a statement like `_::Variant`, you can imagine the underscore to be the type. That means that anything you could do with an actual type like `MyEnum::Variant` will still apply in an infered enum. Ultimately, the enum or struct doesn't need to be imported but, traits and other specfied things will need to be imported. +The `_` token can be used to simplify writing the type name explicitly when the type of its containing expression is known. The elided type does not need to be imported into scope, but if the type is used in the path of an associated trait item, the trait still needs to be imported as `_` only introduces the type itself not the trait. -Due to how the rust compiler currently works, lots of changes will need to be made to allow paths to be infered in an order that allows for all of the mentioned. One issue is getting the type of a given context mentioned in [rust-lang/rust#8995](https://github.com/rust-lang/rust/issues/8995). Finally, here are some examples of non-strict typings that can not be allowed. ```rust @@ -140,7 +139,7 @@ Apple’s Swift had enum inference since 2014 and is not used in most swift code [unresolved-questions]: #unresolved-questions -The implementation of this feature still requires a deep dive into how exactly the compiler should resolve the typings to produce the expected behavior, however, algorithems for finding paths for do an already exist. +The implementation of this feature still requires a deep dive into how exactly the compiler should resolve the typings to produce the expected behaviour, however, algorithms for finding paths for inferred types already exist. # Future possibilities [future-possibilities]: #future-possibilities From d5bb4ebe745ca0aa49fd16425fe765102bcb3a8e Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Tue, 11 Jul 2023 16:23:38 -0700 Subject: [PATCH 09/14] Grammer and file name change --- text/0000-infered-types.md | 148 --------------------------------- text/0000-inferred-types.md | 160 ++++++++++++++++++++++++++++++++++++ 2 files changed, 160 insertions(+), 148 deletions(-) delete mode 100644 text/0000-infered-types.md create mode 100644 text/0000-inferred-types.md diff --git a/text/0000-infered-types.md b/text/0000-infered-types.md deleted file mode 100644 index 0051b6bd424..00000000000 --- a/text/0000-infered-types.md +++ /dev/null @@ -1,148 +0,0 @@ -- Feature Name: Inferred Types -- Start Date: 2023-06-06 -- 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 introduces a feature allowing the base type of enums and structs to be inferred in contexts where strict typing information can be exist. Some examples of strict typing include match statements and function calls. The syntax is `_::EnumVariant` for enums, `_ { a: 1 }` for constructing structs, and `_::method()` for impls and traits where `_` is the type. - - -# Motivation -[motivation]: #motivation - -Rust's goals include clean syntax, that comes with a consice syntax and features like macros making it easier to not repeat yourself. Having to write a type every time you want to do something can be very annoying, repetetive, and not to mention messy. This is a huge problem especialy in large projects with heavy dependency on enums. Additionally, with large libraries developers can expect to import many traits, structures, and enums. One way developers have solved this is by importing everything from specific modules like [`windows-rs`](https://github.com/microsoft/windows-rs). This is problematic because at a glance, it can not be determined where a module comes from. It can be said that developers need a low compromise solution to solve the problem of large imports and messy code. The intent of this RFC’s is to create something that developer friendly yet still conforming to all of rust's goals. Finally, when using specific rust crates, it can be annoying to have to add one package specificaly for a type definition (like `chrono`). Now it can be prevented! - -# Guide-level explanation -[guide-level-explanation]: #guide-level-explanation - -When crating a struct or enum, inferred types can simplify the type into just a underscore. It is important to note, however, that they do not work when the type is not specific enough to be inferred like: type parameters. Below are some examples where they do and don't work. - -Function calls (structs): -```rust -fn my_function(data: MyStruct) { /* ... */ } - -// my_function(MyStruct { -// value: 1 -// }); -my_function(_ { - value: 1 -}); -``` - -Function calls (impl): -```rust -fn my_function(data: MyStruct) { /* ... */ } - -// my_function(MyStruct::new()}); -my_function(_::new()); -``` - -Function returns (enum): -```rust -fn my_function() -> MyEnum { - // MyEnum::MyVarient - _::MyVarient -} -``` - -Match arms: -```rust -enum Example { - One, - Two -} - -fn my_fn(my_enum: Example) -> String { - match my_enum { - _::One => "One!", - _::Two => "Two!" - } -} -``` - -It is important to note that `_` only represents the type; if you have (for example) another enum that can be coerced into the type, you will need to specify it manually. Additionally, any traits required to call an impl will still have to be imported. - -```rust -fn my_function(data: MyStruct) { /* ... */ } - - -my_function(MyStruct2::do_something().into()); // ✅ - - -my_function(_::do_something().into()); // ❌ error[E0599]: variant or associated item not found in `MyStruct` -``` - - -# Reference-level explanation -[reference-level-explanation]: #reference-level-explanation - -The `_` token can be used to simplify writing the type name explicitly when the type of its containing expression is known. The elided type does not need to be imported into scope, but if the type is used in the path of an associated trait item, the trait still needs to be imported as `_` only introduces the type itself not the trait. - - -Finally, here are some examples of non-strict typings that can not be allowed. -```rust -fn convert(argument: T) -> Example {/* ... */} - -do_something(convert(_::new())) -// ^^^^^^^^ Cannot infer type on generic type argument -``` - -However, ones where a generic argument can collapse into strict typing can be allowed. The below works because `T` becomes `Example`. This wouldn’t work if `do_something`, however, took a generic. -```rust -fn do_something(argument: Example) {/* ... */} -fn convert(argument: T) -> T {/* ... */} - -do_something(convert(_::new())) -``` - - -# Drawbacks -[drawbacks]: #drawbacks - -In the thread [[IDEA] Implied enum types](https://internals.rust-lang.org/t/idea-implied-enum-types/18349), many people had a few concerns about this feature. - -These RFCs could create bugs. An example of this is if a function changes has two enum parameters that share common variant names. Because it’s implied, it would still compile with this bug createing unintended behavior wharas by specifying the type names, the compiler would thrown an error. -```rust -enum RadioState { - Disabled, - Enabled, -} - -enum WifiConfig { - Disabled, - Reverse, - Enabled, -} - -fn configure_wireless(radio: RadioState, wifi: WifiConfig) { /* ... */ } -``` - -Another issue with this is that the syntax `_::` could be mistaken for `::` meaning crate. - - -# Rationale and alternatives -[rationale-and-alternatives]: #rationale-and-alternatives - -There have been many ideas for what this operator should be including `::` add `.`. Despite that, the underscore is the best because it has already been used to infer lifetimes and generic types. Additionally the underscore by itself can be used to construct a struct creating a consistent experience. Maintainers should accept this proposal because it can simplify writing Rust code and prevent the large problem of repitition in switch statements. - - -# Prior art -[prior-art]: #prior-art - - -Apple’s Swift had enum inference since 2014 and is not used in most swift codebases with no issues. One thing people have noticed, though, is that it could be used for so much more! That was quite limited and in creating a rust implementation, people need to extend what swift pioneered and make it more universal. That is why this RFC proposes to make the underscore a general operator that can be used outside the small use case of enums and allow it to be used in structs. - -# Unresolved questions -[unresolved-questions]: #unresolved-questions - - -The implementation of this feature still requires a deep dive into how exactly the compiler should resolve the typings to produce the expected behaviour, however, algorithms for finding paths for inferred types already exist. - -# Future possibilities -[future-possibilities]: #future-possibilities - - -I can’t think of anything \ No newline at end of file diff --git a/text/0000-inferred-types.md b/text/0000-inferred-types.md new file mode 100644 index 00000000000..8adb87d414a --- /dev/null +++ b/text/0000-inferred-types.md @@ -0,0 +1,160 @@ +- Feature Name: Inferred Types +- Start Date: 2023-06-06 +- 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 introduces a feature allowing the base type of enumerations and structures to be inferred in contexts where strict typing information already exists. Some examples of strict typing include match statements and function calls. The syntax is `_::EnumVariant` for enumerations, `_ { a: 1 }` for constructing structs, and `_::method()` for implementations and traits, where `_` is the type. + + +# Motivation +[motivation]: #motivation + +Rust's goals include a clean syntax. Because of that, features like macros were created, making it easier to not repeat yourself. Having to write a type path every time you want to do something can be very annoying, repetitive, and, not to mention, hard to read. This is a huge problem, especially in large projects with heavy dependencies on enumerations. Additionally, with large libraries, developers can expect to import many traits, structures, and enumerations. One way developers have solved this is by importing everything from specific modules, like [`windows-rs`](https://github.com/microsoft/windows-rs). This is problematic because, at a glance, it can not be determined where a module comes from. It can be said that developers need a low-compromise solution to solve the problem of large imports and hard-to-read code. The intent of this RFC is to create something that is developer-friendly yet still conforms to all of Rust's goals. Finally, when using specific rust crates, it can be annoying to have to add one package specifically for a type definition (like `chrono`). Now it can be prevented! + +# Guide-level explanation +[guide-level-explanation]: #guide-level-explanation + +When creating a struct or enumeration, inferred types can simplify the type to just an underscore. It is important to note, however, that they do not work when the type is not specific enough to be inferred, like type parameters. Below are some examples of when they do and don't work. + +Function calls (structs): +```rust +struct MyStruct { +   value: usize +} + +fn my_function(data: MyStruct) { /* ... */ } + +// my_function(MyStruct { +//     value: 1 +// }); +my_function(_ { +   value: 1 +}); +``` + +Function calls (impl): +```rust +struct MyStruct {} + +impl MyStruct { +   pub fn new() -> Self { +      Self {} +   } +} + +fn my_function(data: MyStruct) { /* ... */ } + +// my_function(MyStruct::new()}); +my_function(_::new()); +``` + +Function returns (enum): +```rust +fn my_function() -> MyEnum { +   // MyEnum::MyVarient +   _::MyVarient +} +``` + +Match arms: +```rust +enum Example { +   One, +   Two +} + +fn my_fn(my_enum: Example) -> String { +   match my_enum { +      _::One => "One!", +      _::Two => "Two!" +   } +} +``` + +It is important to note that `_` only represents the type; if you have (for example) another enumeration that can be coerced into the type, you will need to specify it manually. Additionally, any traits required to call an implementation will still have to be imported. + +```rust +fn my_function(data: MyStruct) { /* ... */ } + + +my_function(MyStruct2::do_something().into()); // ✅ + + +my_function(_::do_something().into()); // ❌ error[E0599]: variant or associated item not found in `MyStruct` +``` + + +# Reference-level explanation +[reference-level-explanation]: #reference-level-explanation + +The `_` token can be used to simplify writing the type name explicitly when the type of its containing expression is known. The elided type does not need to be imported into scope, but if the type is used in the path of an associated trait item, the trait still needs to be imported as `_` only introduces the type itself, not the trait. + + +Finally, here are some examples of non-strict typings that can not be allowed. +```rust +fn convert(argument: T) -> Example {/* ... */} + +do_something(convert(_::new())) +//                   ^^^^^^^^ Cannot infer type on generic type argument +``` + +However, ones where a generic argument can collapse into strict typing can be allowed. The below works because `T` becomes `Example`. This wouldn’t work if `do_something`, however, took a generic. +```rust +fn do_something(argument: Example) {/* ... */} +fn convert(argument: T) -> T {/* ... */} + +do_something(convert(_::new())) +``` + + +# Drawbacks +[drawbacks]: #drawbacks + +In the thread [[IDEA] Implied enum types](https://internals.rust-lang.org/t/idea-implied-enum-types/18349), many people had a few concerns about this feature. + +These RFCs could cause bugs. An example of this is if a function has two enumeration parameters that share common variant names. Because it’s implied, it would still compile with this bug, creating unintended behavior, whereas by specifying the type names, the compiler would have thrown an error. +```rust +enum RadioState { +   Disabled, +   Enabled, +} + +enum WifiConfig { +   Disabled, +   Reverse, +   Enabled, +} + +fn configure_wireless(radio: RadioState, wifi: WifiConfig) { /* ... */ } +``` + +Another issue with this is that the syntax `_::` could be mistaken for `::crate_name` meaning a crate path. + + +# Rationale and alternatives +[rationale-and-alternatives]: #rationale-and-alternatives + +There have been many ideas for what this operator should be, including `::` and `.`. Despite that, the underscore is the best because it has already been used to infer lifetimes and generic types. Additionally, the underscore by itself can be used to construct a structure, creating a consistent experience. Maintainers should accept this proposal because it can simplify writing Rust code and prevent the large problem of repetition in switch statements. + + +# Prior art +[prior-art]: #prior-art + + +Apple’s Swift has had enumeration inference since 2014 and is used in many Swift codebases. One thing people have noticed, though, is that it could be used for so much more! In creating a Rust implementation, the goal was to extend what Swift pioneered and make it more universal. That is why this RFC proposes to make the underscore a general operator that can be used outside the small use case of enumerations and allow it to be used in structs. + +# Unresolved questions +[unresolved-questions]: #unresolved-questions + + +The implementation of this feature still requires a deep dive into how exactly the compiler should resolve the typings to produce the expected behavior, however, algorithms for finding paths for inferred types already exist. + +# Future possibilities +[future-possibilities]: #future-possibilities + + +I can’t think of anything. \ No newline at end of file From b1b3b2fc9098a7a812af8c4cd23a345aa4c8e555 Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Mon, 28 Aug 2023 21:18:20 -0700 Subject: [PATCH 10/14] `_::method()` is not allowed --- text/0000-inferred-types.md | 88 ++++++++++++++++++++++--------------- 1 file changed, 53 insertions(+), 35 deletions(-) diff --git a/text/0000-inferred-types.md b/text/0000-inferred-types.md index 8adb87d414a..0c361a5390c 100644 --- a/text/0000-inferred-types.md +++ b/text/0000-inferred-types.md @@ -7,7 +7,7 @@ # Summary [summary]: #summary -This RFC introduces a feature allowing the base type of enumerations and structures to be inferred in contexts where strict typing information already exists. Some examples of strict typing include match statements and function calls. The syntax is `_::EnumVariant` for enumerations, `_ { a: 1 }` for constructing structs, and `_::method()` for implementations and traits, where `_` is the type. +This RFC introduces a feature allowing the base type of enumerations and structures to be inferred in contexts where strict typing information already exists. Some examples of strict typing include match statements and function calls. The syntax is `_::EnumVariant` for enumerations and `_ { a: 1 }` for constructing structs. # Motivation @@ -18,7 +18,7 @@ Rust's goals include a clean syntax. Because of that, features like macros were # Guide-level explanation [guide-level-explanation]: #guide-level-explanation -When creating a struct or enumeration, inferred types can simplify the type to just an underscore. It is important to note, however, that they do not work when the type is not specific enough to be inferred, like type parameters. Below are some examples of when they do and don't work. +When creating a struct or enumeration, inferred types can simplify the type to just an underscore. It is important to note, however, that they do not allow any sort of interaction with implementations and they don't work when the type is not specific enough to be inferred, like type parameters. Below are some examples of when they do and don't work. Function calls (structs): ```rust @@ -36,22 +36,6 @@ my_function(_ { }); ``` -Function calls (impl): -```rust -struct MyStruct {} - -impl MyStruct { -   pub fn new() -> Self { -      Self {} -   } -} - -fn my_function(data: MyStruct) { /* ... */ } - -// my_function(MyStruct::new()}); -my_function(_::new()); -``` - Function returns (enum): ```rust fn my_function() -> MyEnum { @@ -75,42 +59,77 @@ fn my_fn(my_enum: Example) -> String { } ``` -It is important to note that `_` only represents the type; if you have (for example) another enumeration that can be coerced into the type, you will need to specify it manually. Additionally, any traits required to call an implementation will still have to be imported. +It is important to note that `_` only represents the type; if you have (for example) another enumeration that can be coerced into the type, you will need to specify it manually. ```rust -fn my_function(data: MyStruct) { /* ... */ } +enum MyEnum { + One, + Two +} +enum Example { + Alpha, + Bravo +} + +impl Into for Example { + fn into(self) -> MyEnum { + match self { + _::Alpha => _::One, + _::Bravo => _::Two + } + } +} -my_function(MyStruct2::do_something().into()); // ✅ +fn my_function(data: MyEnum) { /* ... */ } -my_function(_::do_something().into()); // ❌ error[E0599]: variant or associated item not found in `MyStruct` +my_function(Example::Alpha.into()); // ✅ + + +my_function(_::Alpha().into()); // ❌ ``` # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -The `_` token can be used to simplify writing the type name explicitly when the type of its containing expression is known. The elided type does not need to be imported into scope, but if the type is used in the path of an associated trait item, the trait still needs to be imported as `_` only introduces the type itself, not the trait. +The `_` token can be used to simplify writing the type name explicitly when the type of its containing expression is known. The elided type does not need to be imported into scope. The `_` token does not allow access to implementations and trait implementations of any sort. - -Finally, here are some examples of non-strict typings that can not be allowed. +Mentioned above, implementations of any sort are not allowed ```rust -fn convert(argument: T) -> Example {/* ... */} +#[derive(Default)] +struct MyStruct { + test: String +} -do_something(convert(_::new())) -//                   ^^^^^^^^ Cannot infer type on generic type argument +impl MyStruct { + fn new() -> Self { + Self { + test: "Hello!" + } + } +} + +fn do_something(argument: MyStruct) {/* ... */} + +do_something(_::default()) +// ^^^^^^^^^^^^ Cannot call implementations methods on infered types. +do_something(_::new()) +// ^^^^^^ Cannot call implementations methods on infered types. ``` -However, ones where a generic argument can collapse into strict typing can be allowed. The below works because `T` becomes `Example`. This wouldn’t work if `do_something`, however, took a generic. + +Finally, here are some examples of non-strict typings that can not be allowed. ```rust -fn do_something(argument: Example) {/* ... */} -fn convert(argument: T) -> T {/* ... */} +fn do_something(argument: T) -> Example {/* ... */} -do_something(convert(_::new())) +do_something(_ { test: "Hello" }) +//           ^^^^^^^^^^^^^^^^^^^ Cannot infer type on generic type argument ``` + # Drawbacks [drawbacks]: #drawbacks @@ -134,6 +153,7 @@ fn configure_wireless(radio: RadioState, wifi: WifiConfig) { /* ... */ } Another issue with this is that the syntax `_::` could be mistaken for `::crate_name` meaning a crate path. +Additionaly, the `_` opperator could confuse new users because new users may try to use the `_` opperator to try to access implementation methods. # Rationale and alternatives [rationale-and-alternatives]: #rationale-and-alternatives @@ -150,11 +170,9 @@ Apple’s Swift has had enumeration inference since 2014 and is used in many Swi # Unresolved questions [unresolved-questions]: #unresolved-questions - The implementation of this feature still requires a deep dive into how exactly the compiler should resolve the typings to produce the expected behavior, however, algorithms for finding paths for inferred types already exist. # Future possibilities [future-possibilities]: #future-possibilities - -I can’t think of anything. \ No newline at end of file +Maybe in the future, implementation methods calls could be allowed. \ No newline at end of file From 6311c499858362428e65c0fb3ff1794ffb740914 Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Fri, 17 Nov 2023 12:15:49 -0800 Subject: [PATCH 11/14] Spelling correction Co-authored-by: Josh Triplett --- text/0000-inferred-types.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/text/0000-inferred-types.md b/text/0000-inferred-types.md index 0c361a5390c..c6314b83369 100644 --- a/text/0000-inferred-types.md +++ b/text/0000-inferred-types.md @@ -39,8 +39,8 @@ my_function(_ { Function returns (enum): ```rust fn my_function() -> MyEnum { -   // MyEnum::MyVarient -   _::MyVarient +   // MyEnum::MyVariant +   _::MyVariant } ``` From 55f80661ff0e8b50bfe3f411e4ce41373d254f4c Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Wed, 3 Jul 2024 14:04:29 -0400 Subject: [PATCH 12/14] Rewrite and fully outline (in more detail) how this feature will work --- text/0000-inferred-types.md | 158 +++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 75 deletions(-) diff --git a/text/0000-inferred-types.md b/text/0000-inferred-types.md index c6314b83369..a81ee76f343 100644 --- a/text/0000-inferred-types.md +++ b/text/0000-inferred-types.md @@ -1,73 +1,81 @@ + + + - Feature Name: Inferred Types + - Start Date: 2023-06-06 + - 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) +- Rust Issue: [rust-lang/rust#0000](https://github.com/rust-lang/rust/issues/0000) # Summary -[summary]: #summary -This RFC introduces a feature allowing the base type of enumerations and structures to be inferred in contexts where strict typing information already exists. Some examples of strict typing include match statements and function calls. The syntax is `_::EnumVariant` for enumerations and `_ { a: 1 }` for constructing structs. +[summary]: #summary +This RFC introduces type inference via the underscore (`_`) syntax. Developers will be able to use the underscore (`_`) operator instead of writing out type names (e.g., `MyStruct`) to infer and construct `enum`s and `struct`s in function calls, match arms, and other places where significant type information is available. With this RFC, the following syntax will be available: `_::EnumVariant` and `_ { struct_field: 1 }`. # Motivation + [motivation]: #motivation -Rust's goals include a clean syntax. Because of that, features like macros were created, making it easier to not repeat yourself. Having to write a type path every time you want to do something can be very annoying, repetitive, and, not to mention, hard to read. This is a huge problem, especially in large projects with heavy dependencies on enumerations. Additionally, with large libraries, developers can expect to import many traits, structures, and enumerations. One way developers have solved this is by importing everything from specific modules, like [`windows-rs`](https://github.com/microsoft/windows-rs). This is problematic because, at a glance, it can not be determined where a module comes from. It can be said that developers need a low-compromise solution to solve the problem of large imports and hard-to-read code. The intent of this RFC is to create something that is developer-friendly yet still conforms to all of Rust's goals. Finally, when using specific rust crates, it can be annoying to have to add one package specifically for a type definition (like `chrono`). Now it can be prevented! +Rust’s goals include having a concise syntax. Features such as macros were created for this reason; developers shouldn’t have to repeat themselves. Like this, having to write types to match arms, functions, and locations where the type is obvious can be both frustrating, repetitive, hard to read, and time-consuming. Existing solutions such as importing everything or importing types with a single character don’t cut it, as importing everything makes it hard to determine where types come from, and renaming types makes it hard to read. This problem is prevalent in libraries such as [`actix-web`](https://github.com/actix/actix-web/blob/e189e4a3bf60edeff5b5259d4f60488d943eebec/actix-http/src/ws/codec.rs#L123), it's inconvenient to write and hard to read the same type over and over. Developers need a low-compromise solution that is both readable and concise. This is the goal of this RFC: developing an agreeable syntax. # Guide-level explanation + [guide-level-explanation]: #guide-level-explanation -When creating a struct or enumeration, inferred types can simplify the type to just an underscore. It is important to note, however, that they do not allow any sort of interaction with implementations and they don't work when the type is not specific enough to be inferred, like type parameters. Below are some examples of when they do and don't work. +When constructing a `struct` or `enum`, inferred types can lower the verbosity of the code; you don’t need to type the type name for everything. You can use the `_` operator instead, with `_::EnumVariant` for `enum`s and `_ { struct_field: 1 }` for `struct`s. + +Note: calling methods on `impl`s and `impl` traits are not supported. -Function calls (structs): +Function calls: ```rust -struct MyStruct { -   value: usize +struct AppSettings { + pub enable_foobar: bool } -fn my_function(data: MyStruct) { /* ... */ } +fn update_settings(data: AppSettings) { /* ... */ } -// my_function(MyStruct { -//     value: 1 +// update_settings(AppSettings { +// enable_foobar: true // }); -my_function(_ { -   value: 1 +update_settings(_ { + enable_foobar: true }); ``` -Function returns (enum): +Function returns: ```rust fn my_function() -> MyEnum { -   // MyEnum::MyVariant -   _::MyVariant + // MyEnum::MyVariant + _::MyVariant } ``` Match arms: ```rust enum Example { -   One, -   Two + One, + Two } fn my_fn(my_enum: Example) -> String { -   match my_enum { -      _::One => "One!", -      _::Two => "Two!" -   } + match my_enum { + _::One => "One!", + _::Two => "Two!" + } } ``` -It is important to note that `_` only represents the type; if you have (for example) another enumeration that can be coerced into the type, you will need to specify it manually. - +Note: that `_` only represents the type inferred from the function. Other types will have to be manually specified. ```rust -enum MyEnum { +enum Numbers { One, Two } -enum Example { +enum Letters { Alpha, Bravo } @@ -81,98 +89,98 @@ impl Into for Example { } } -fn my_function(data: MyEnum) { /* ... */ } +fn use_numbers(number: Numbers) { /* ... */ } +use_numbers(Example::Alpha.into()); +// ^^^^^^^^^^^^^^^^^^^^^ Valid code -my_function(Example::Alpha.into()); // ✅ - - -my_function(_::Alpha().into()); // ❌ +use_numbers(_::Alpha.into()); +// ^^^^^^^^^^^^^^^^^ error[E0599]: no variant or associated item named `Alpha` found for enum `Numbers` in the current scope ``` - # Reference-level explanation -[reference-level-explanation]: #reference-level-explanation -The `_` token can be used to simplify writing the type name explicitly when the type of its containing expression is known. The elided type does not need to be imported into scope. The `_` token does not allow access to implementations and trait implementations of any sort. +[reference-level-explanation]: #reference-level-explanation -Mentioned above, implementations of any sort are not allowed -```rust -#[derive(Default)] +The underscore (`_`) syntax is designed to allow concise writing of rust code. Most of the time, it will just like when the type is written in a verbose way. The compiler will first look for the underscore token (`_`) followed by, optionally, type parameters (`::`), then brackets (for `struct`s) or a variant (for `enum`s). Below are some variations of valid syntax. + +```rust +_::<&'static str>::EnumVariant(&"Hello, rust") +_::<&'static str> { + field: &"Hello, rust" +} +_::EnumVariant +_ { + field: 1 +} +``` + +Unlike explicitly writing the type, the underscore (`_`) syntax does not support accessing `impl` and `impl` trait data. This is because return types from `impl` methods may be different from the expected type of the variable, match arm, function call, etc… When the compiler encounters an attempt at accessing data that may be on an `impl`, it should recommend an explicit type declaration. Below is an example: + +```rust +#[derive(Default)] struct MyStruct { - test: String -} - -impl MyStruct { - fn new() -> Self { - Self { - test: "Hello!" - } - } + value: usize } -fn do_something(argument: MyStruct) {/* ... */} - -do_something(_::default()) -// ^^^^^^^^^^^^ Cannot call implementations methods on infered types. -do_something(_::new()) -// ^^^^^^ Cannot call implementations methods on infered types. -``` - - -Finally, here are some examples of non-strict typings that can not be allowed. -```rust -fn do_something(argument: T) -> Example {/* ... */} - -do_something(_ { test: "Hello" }) -//           ^^^^^^^^^^^^^^^^^^^ Cannot infer type on generic type argument -``` - +let test: MyStruct = _::default(); +// ^^^^^^^^^^ (example) Cannot access impl methods on inferred types. Help: replace `_` with ` MyStruct`. +``` +Anywhere where a type can be inferred, this syntax is allowed. This includes variable definitions, match arms, function calls, struct fields, and enum variants. # Drawbacks + [drawbacks]: #drawbacks -In the thread [[IDEA] Implied enum types](https://internals.rust-lang.org/t/idea-implied-enum-types/18349), many people had a few concerns about this feature. +In the thread [[IDEA] Implied enum types](https://internals.rust-lang.org/t/idea-implied-enum-types/18349), many people had a few concerns about this feature. + +This feature could cause bugs. An example of this is if a function has two enumeration parameters that share common variant names. Using implied types here would make it impossible to know which settings are being set to what (without looking at the function signature). -These RFCs could cause bugs. An example of this is if a function has two enumeration parameters that share common variant names. Because it’s implied, it would still compile with this bug, creating unintended behavior, whereas by specifying the type names, the compiler would have thrown an error. ```rust enum RadioState { -   Disabled, -   Enabled, + Disabled, + Enabled, } enum WifiConfig { -   Disabled, -   Reverse, -   Enabled, + Disabled, + Reverse, + Enabled, } fn configure_wireless(radio: RadioState, wifi: WifiConfig) { /* ... */ } + +// Hard to understand without function signature +configure_wireless(_::Disabled, _::Enable); ``` Another issue with this is that the syntax `_::` could be mistaken for `::crate_name` meaning a crate path. -Additionaly, the `_` opperator could confuse new users because new users may try to use the `_` opperator to try to access implementation methods. +Additionally, the `_` operator could confuse new users because new users may try to use the `_` operator to try to access implementation methods. # Rationale and alternatives + [rationale-and-alternatives]: #rationale-and-alternatives -There have been many ideas for what this operator should be, including `::` and `.`. Despite that, the underscore is the best because it has already been used to infer lifetimes and generic types. Additionally, the underscore by itself can be used to construct a structure, creating a consistent experience. Maintainers should accept this proposal because it can simplify writing Rust code and prevent the large problem of repetition in switch statements. +There have been many ideas for what this operator should be, including `::` and `.`. Despite that, the underscore is the best because it has already been used to infer lifetimes and generic types. Additionally, the underscore by itself can be used to construct `struct`s, creating a consistent experience. +As mentioned in the motivation, this would make code more concise and easier to read by removing the need to repeat type names already obvious. # Prior art -[prior-art]: #prior-art +[prior-art]: #prior-art Apple’s Swift has had enumeration inference since 2014 and is used in many Swift codebases. One thing people have noticed, though, is that it could be used for so much more! In creating a Rust implementation, the goal was to extend what Swift pioneered and make it more universal. That is why this RFC proposes to make the underscore a general operator that can be used outside the small use case of enumerations and allow it to be used in structs. # Unresolved questions + [unresolved-questions]: #unresolved-questions -The implementation of this feature still requires a deep dive into how exactly the compiler should resolve the typings to produce the expected behavior, however, algorithms for finding paths for inferred types already exist. +The implementation of this feature still requires a deep dive into how exactly the compiler should resolve the typings. It may require a rewrite of how types are resolved within the compiler. # Future possibilities + [future-possibilities]: #future-possibilities -Maybe in the future, implementation methods calls could be allowed. \ No newline at end of file +In the future, implementation methods calls could be allowed in certain cases with certain rules. From 0338f94a55f4e41054e3f1e7e95508aec5c01aba Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Wed, 3 Jul 2024 14:06:34 -0400 Subject: [PATCH 13/14] Remove formating changes done by Microsoft Word. --- text/0000-inferred-types.md | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/text/0000-inferred-types.md b/text/0000-inferred-types.md index a81ee76f343..4e9e55eb390 100644 --- a/text/0000-inferred-types.md +++ b/text/0000-inferred-types.md @@ -1,28 +1,19 @@ - - - - Feature Name: Inferred Types - - Start Date: 2023-06-06 - - 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 introduces type inference via the underscore (`_`) syntax. Developers will be able to use the underscore (`_`) operator instead of writing out type names (e.g., `MyStruct`) to infer and construct `enum`s and `struct`s in function calls, match arms, and other places where significant type information is available. With this RFC, the following syntax will be available: `_::EnumVariant` and `_ { struct_field: 1 }`. # Motivation - [motivation]: #motivation Rust’s goals include having a concise syntax. Features such as macros were created for this reason; developers shouldn’t have to repeat themselves. Like this, having to write types to match arms, functions, and locations where the type is obvious can be both frustrating, repetitive, hard to read, and time-consuming. Existing solutions such as importing everything or importing types with a single character don’t cut it, as importing everything makes it hard to determine where types come from, and renaming types makes it hard to read. This problem is prevalent in libraries such as [`actix-web`](https://github.com/actix/actix-web/blob/e189e4a3bf60edeff5b5259d4f60488d943eebec/actix-http/src/ws/codec.rs#L123), it's inconvenient to write and hard to read the same type over and over. Developers need a low-compromise solution that is both readable and concise. This is the goal of this RFC: developing an agreeable syntax. # Guide-level explanation - [guide-level-explanation]: #guide-level-explanation When constructing a `struct` or `enum`, inferred types can lower the verbosity of the code; you don’t need to type the type name for everything. You can use the `_` operator instead, with `_::EnumVariant` for `enum`s and `_ { struct_field: 1 }` for `struct`s. @@ -99,7 +90,6 @@ use_numbers(_::Alpha.into()); ``` # Reference-level explanation - [reference-level-explanation]: #reference-level-explanation The underscore (`_`) syntax is designed to allow concise writing of rust code. Most of the time, it will just like when the type is written in a verbose way. The compiler will first look for the underscore token (`_`) followed by, optionally, type parameters (`::`), then brackets (for `struct`s) or a variant (for `enum`s). Below are some variations of valid syntax. @@ -130,7 +120,6 @@ let test: MyStruct = _::default(); Anywhere where a type can be inferred, this syntax is allowed. This includes variable definitions, match arms, function calls, struct fields, and enum variants. # Drawbacks - [drawbacks]: #drawbacks In the thread [[IDEA] Implied enum types](https://internals.rust-lang.org/t/idea-implied-enum-types/18349), many people had a few concerns about this feature. @@ -160,7 +149,6 @@ Another issue with this is that the syntax `_::` could be mistaken for `::crate_ Additionally, the `_` operator could confuse new users because new users may try to use the `_` operator to try to access implementation methods. # Rationale and alternatives - [rationale-and-alternatives]: #rationale-and-alternatives There have been many ideas for what this operator should be, including `::` and `.`. Despite that, the underscore is the best because it has already been used to infer lifetimes and generic types. Additionally, the underscore by itself can be used to construct `struct`s, creating a consistent experience. @@ -174,13 +162,11 @@ As mentioned in the motivation, this would make code more concise and easier to Apple’s Swift has had enumeration inference since 2014 and is used in many Swift codebases. One thing people have noticed, though, is that it could be used for so much more! In creating a Rust implementation, the goal was to extend what Swift pioneered and make it more universal. That is why this RFC proposes to make the underscore a general operator that can be used outside the small use case of enumerations and allow it to be used in structs. # Unresolved questions - [unresolved-questions]: #unresolved-questions The implementation of this feature still requires a deep dive into how exactly the compiler should resolve the typings. It may require a rewrite of how types are resolved within the compiler. # Future possibilities - [future-possibilities]: #future-possibilities In the future, implementation methods calls could be allowed in certain cases with certain rules. From 8d44a919bc14821db06abdd24571d70a5a78fe9c Mon Sep 17 00:00:00 2001 From: Joshua Brest <36625023+JoshuaBrest@users.noreply.github.com> Date: Thu, 4 Jul 2024 12:56:55 -0700 Subject: [PATCH 14/14] Add examples and remove detail on compiler errors in Reference-level explanation. --- text/0000-inferred-types.md | 67 +++++++++++++++++++++++++++++++++++-- 1 file changed, 64 insertions(+), 3 deletions(-) diff --git a/text/0000-inferred-types.md b/text/0000-inferred-types.md index 4e9e55eb390..beef485b349 100644 --- a/text/0000-inferred-types.md +++ b/text/0000-inferred-types.md @@ -92,7 +92,7 @@ use_numbers(_::Alpha.into()); # Reference-level explanation [reference-level-explanation]: #reference-level-explanation -The underscore (`_`) syntax is designed to allow concise writing of rust code. Most of the time, it will just like when the type is written in a verbose way. The compiler will first look for the underscore token (`_`) followed by, optionally, type parameters (`::`), then brackets (for `struct`s) or a variant (for `enum`s). Below are some variations of valid syntax. +The underscore (`_`) syntax is designed to allow concise writing of rust code. Most of the time, it is just like when the type is written explicitly. The compiler will first look for the underscore token (`_`) followed by, optionally, type parameters (`::`), then brackets (for `struct`s) or a variant (for `enum`s). Below are some variations of valid syntax. ```rust _::<&'static str>::EnumVariant(&"Hello, rust") @@ -105,7 +105,7 @@ _ { } ``` -Unlike explicitly writing the type, the underscore (`_`) syntax does not support accessing `impl` and `impl` trait data. This is because return types from `impl` methods may be different from the expected type of the variable, match arm, function call, etc… When the compiler encounters an attempt at accessing data that may be on an `impl`, it should recommend an explicit type declaration. Below is an example: +Unlike explicitly writing the type, the underscore (`_`) syntax does not support accessing `impl` and `impl` trait data. This is because return types from `impl` methods may be different from the expected type of the variable, match arm, function call, etc… ```rust #[derive(Default)] @@ -117,7 +117,68 @@ let test: MyStruct = _::default(); // ^^^^^^^^^^ (example) Cannot access impl methods on inferred types. Help: replace `_` with ` MyStruct`. ``` -Anywhere where a type can be inferred, this syntax is allowed. This includes variable definitions, match arms, function calls, struct fields, and enum variants. +Below are the places where types can be inferred, along with the `struct`s and `enum`s used for the examples. + +Type definitions: +```rust +use std::default::Default; + +#[derive(Default)] +struct FerrisData { + pub alive: bool, + pub mood: FerrisMood +} + +#[derive(Default)] +enum FerrisMood { + #[default] + Happy, + Sad, + Angry, + Unknown(Option) +} +``` + + +Inferable locations: +- Variable Definitions: + ```rust + let value: usize = 2; + let data: FerrisMood = match value { + 0 => _::Happy, + 1 => _::Sad, + 2 => _::Angry, + num => _::Unknown(Some(num)) + }; + let happy: FerrisMood = _::Happy; + ``` +- Match arms: + ```rust + let value: FerrisMood = _::Happy; + let data = match value { + _::Happy => 0, + _::Sad => 1, + _::Angry => 2, + _::Unknown(option) => option.unwrap_or(3) + }; + ``` +- Function calls: + ```rust + fn say_mood(mood: FerrisMood) {} + say_mood(_::Happy); + ``` +- Struct fields: + ```rust + let data = FerrisData { + mood: _::Happy, + ..FerrisData::default() + }; + ``` +- Enum variants: + ```rust + let data = FerrisMood::Unknown(_::Some(1)); + ``` + # Drawbacks [drawbacks]: #drawbacks