Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inferred types _::Enum #3444

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
174 changes: 174 additions & 0 deletions text/0000-infered-types.md
Original file line number Diff line number Diff line change
@@ -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`.
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved


# 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To just call a function on an implementation can take upwards of 3 imports.

You should probably provide an example to back up this claim. I understand importing an extension trait on occasion, but my experience does not match this.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Traits and stuff, I'll make sure to add it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps this should also mention the common rust idiom of a library prelude. Or at least that should be mentioned in an alternatives section.

JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved

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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They can be used in places where strong typing exists.

Rust is strongly typed. I think this should be more precise, like saying "where type inference has enough information to deduce the base type" or something.

Alternatively, it may be better to exhaustively specify positions (like we do with coercion sites: https://doc.rust-lang.org/reference/type-coercions.html) where it's possible to use this syntax.


Function calls (struct):
```rust
fn my_function(data: MyStruct) { /* ... */ }

// my_function(MyStruct {
// value: 1
// });
my_function(_ {
value: 1
});
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved
```

Function calls (impl):
```rust
fn my_function(data: MyStruct) { /* ... */ }

// my_function(MyStruct::new()});
my_function(_::new());
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved
```
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved

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`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just another example of "not enough type information" that we already get in Rust. I don't think that this needs special treatment in the RFC.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is a real error thrown in rust because they meant to sepcify MyStruct2 but _ refers to MyStruct

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you leave some more explanation in the RFC for what you mean here? I'm having trouble understanding the point you're trying to make in this section.

```

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.
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved


# Reference-level explanation
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved
[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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

The RFC should probably not comment on how easy it is to implement. This would need changes (at least) to the parser, HIR lowering, HIR typeck, method resolution, etc, all of which aren't necessarily a walk in the park to modify.

I don't expect it to be easy, but it's probably possible at least in a limited form (some parts easier than others). But yeah, the RFC should probably just leave it unstated.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Anywhere with a strongly typed argument as mentioned above can be inferred.

Mentioning this again, this is a bit vague.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are examples below.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. I think a reference-level explanation should be reference-like, not just have some examples.
  2. These examples aren't even exhaustive 😅


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<T>(argument: T) -> Example {/* ... */}

do_something(convert(_::new()))
// ^^^^^^^^ Cannot infer type on generic type argument
```
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved

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<T>(argument: T) -> T {/* ... */}

do_something(convert(_::new()))
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved
```
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved


# 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.
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved

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) { /* ... */ }
```
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved

Another issue with this is that the syntax `_::` could be mistaken for `::` meaning crate.
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved


# 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.
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved


# 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.
JoshuaBrest marked this conversation as resolved.
Show resolved Hide resolved


# 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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this question needs to be resolved before the RFC is landed, since it pretty drastically changes the implementation and behavior of the RFC.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would love to discuss it (:



# Future possibilities
[future-possibilities]: #future-possibilities


I can’t think of anything