-
Notifications
You must be signed in to change notification settings - Fork 1.6k
RFC: impl trait expressions #2604
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,148 @@ | ||||||
- Feature Name: `impl_trait_expressions` | ||||||
- Start Date: 2018-12-03 | ||||||
- RFC PR: (leave this empty) | ||||||
- Rust Issue: (leave this empty) | ||||||
|
||||||
# Summary | ||||||
[summary]: #summary | ||||||
|
||||||
Rust closures allow the programmer to create values of anonymous types which | ||||||
implement the `Fn*` traits. This RFC proposes a generalisation of this feature | ||||||
to other traits. The syntax looks like this: | ||||||
|
||||||
```rust | ||||||
fn main() { | ||||||
let world = "world"; | ||||||
let says_hello_world = impl fmt::Display { | ||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | ||||||
write!(f, "hello {}", world) | ||||||
} | ||||||
}; | ||||||
|
||||||
println!("{}", says_hello_world); | ||||||
} | ||||||
``` | ||||||
|
||||||
# Motivation | ||||||
[motivation]: #motivation | ||||||
|
||||||
Sometimes we need to create a once-off value which implements some trait, | ||||||
though having to explicitly declare a type in these situations can be | ||||||
unnecessarily painful and noisy. Closures are a good example of how | ||||||
this problem can be ameliorated by adding the ability to declare once-off | ||||||
values of anonymous types. | ||||||
|
||||||
# Guide-level explanation | ||||||
[guide-level-explanation]: #guide-level-explanation | ||||||
|
||||||
`impl Trait` expressions allow the user to create a value of an anonymous type which | ||||||
implements `Trait`. These expressions behave the same as closures - they | ||||||
capture local variables, by either move or reference, and collect them into an | ||||||
anonymous struct which implements the required trait. | ||||||
|
||||||
To better understand the behaviour of `impl Trait`, consider the following code: | ||||||
|
||||||
```rust | ||||||
let y = String::from("hello"); | ||||||
let foo = move || println!("{}", y); | ||||||
``` | ||||||
|
||||||
With this RFC, the above code becomes syntax sugar for: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't think calling it sugar does it justice; in particular, if you add type parameters to a closure, there's type inference going on to infer what the type of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
```rust | ||||||
let y = String::from("hello"); | ||||||
let foo = move impl FnOnce<()> { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Mind that the type parameters of the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably be written as |
||||||
type Output = (); | ||||||
|
||||||
extern "rust-call" fn call_once(self, args: ()) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Aaaa I keep being reminded I need to fix this (it should ideally be just |
||||||
println!("{}", y); | ||||||
} | ||||||
}; | ||||||
``` | ||||||
|
||||||
Which, in turn, is syntax sugar for: | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
|
||||||
```rust | ||||||
let y = String::from("hello"); | ||||||
|
||||||
struct MyCoolAnonType { | ||||||
y: String, | ||||||
} | ||||||
|
||||||
impl FnOnce<()> for MyCoolAnonType { | ||||||
type Output = (); | ||||||
|
||||||
extern "rust-call" fn call_once(self, args: ()) { | ||||||
println!("{}", self.y); | ||||||
} | ||||||
} | ||||||
|
||||||
let foo = MyCoolAnonType { y }; | ||||||
``` | ||||||
|
||||||
# Reference-level explanation | ||||||
[reference-level-explanation]: #reference-level-explanation | ||||||
|
||||||
This feature is fully described in the guide-level explanation. As this is a | ||||||
generalisation of the existing closure syntax I suspect that the implementation | ||||||
would be fairly straight-forward. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd like at least the following to be discussed:
|
||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As for the ambiguity aforementioned, consider: fn foo() {
struct X;
impl X {
fn foo() {}
};
} This compiles today and According to your RFC however This is not an insurmountable challenge but you'll need to think about how to deal with it. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. cc @petrochenkov @eddyb @qmx ^ ideas? |
||||||
# Drawbacks | ||||||
[drawbacks]: #drawbacks | ||||||
|
||||||
Adds yet another feature to an already-rich language. | ||||||
|
||||||
# Rationale and alternatives | ||||||
[rationale-and-alternatives]: #rationale-and-alternatives | ||||||
|
||||||
Not do this. | ||||||
|
||||||
# Prior art | ||||||
[prior-art]: #prior-art | ||||||
|
||||||
Other than closures I'm not aware of any prior art. | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Some prior art to consider:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Kotlin and C++ also have a similar feature to Java's. |
||||||
|
||||||
# Unresolved questions | ||||||
[unresolved-questions]: #unresolved-questions | ||||||
|
||||||
None. | ||||||
|
||||||
# Future possibilities | ||||||
[future-possibilities]: #future-possibilities | ||||||
|
||||||
It would be good if both closures and `impl Trait` expressions could implement | ||||||
traits generically. For example we should be able to write: | ||||||
|
||||||
```rust | ||||||
let mut count = 0u32; | ||||||
let print_numbered = move <T: Display> |val: T| { | ||||||
println!("{}: {}", count, val); | ||||||
count += 1; | ||||||
}; | ||||||
``` | ||||||
|
||||||
Or, more verbosely: | ||||||
|
||||||
```rust | ||||||
let mut count = 0u32; | ||||||
let print_numbered = impl<T: Display> FnOnce<(T,)> { | ||||||
type Output = (); | ||||||
|
||||||
extern "rust-call" fn call_once(self, (val,): (T,)) { | ||||||
println!("{}: {}", count, val); | ||||||
count += 1; | ||||||
} | ||||||
}; | ||||||
``` | ||||||
|
||||||
To define a value that can be called with any `Display` type: | ||||||
|
||||||
```rust | ||||||
print_numbered(123); | ||||||
print_numbered("hello"); | ||||||
|
||||||
// prints: | ||||||
// 0: 123 | ||||||
// 1: hello | ||||||
``` | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The motivation feels a bit thin; it would be good to work in some real world use cases of where it would be beneficial; I'm sure you won't have any problems doing that.