From 82b65b9bf80f6cecbded445c08df5560c359a6df Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Wed, 15 Jun 2016 14:22:02 +0100 Subject: [PATCH 1/5] Generic closures --- text/0000-generic-closures.md | 80 +++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 text/0000-generic-closures.md diff --git a/text/0000-generic-closures.md b/text/0000-generic-closures.md new file mode 100644 index 00000000000..86fe370908c --- /dev/null +++ b/text/0000-generic-closures.md @@ -0,0 +1,80 @@ +- Feature Name: generic_closure +- Start Date: 2015-06-15 +- RFC PR: (leave this empty) +- Rust Issue: (leave this empty) + +# Summary +[summary]: #summary + +This RFC adds the ability to define closures that are generic over types. + +# Motivation +[motivation]: #motivation + +Generic closures can be used to support compound operations on tuple types: + +```rust +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +struct Tuple(pub A, pub B, pub C); + +impl Tuple { + fn map(self, mut f: F) -> Tuple + where F: FnMut(A) -> A2 + FnMut(B) -> B2 + FnMut(C) -> C2 + { + Tuple(f(self.0), f(self.1), f(self.2)) + } + + fn fold(self, val: T, mut f: F) -> T + where F: FnMut(T, A) -> T + FnMut(T, B) -> T + FnMut(T, C) -> T + { + let val = f(val, self.0); + let val = f(val, self.1); + let val = f(val, self.2); + val + } +} + +let a = Tuple(1u8, 2u32, 3.5f32).map(>|x: T| x.into() + 1.0); +assert_eq!(a, (2f64, 3f64, 4.5f64)); + +let b = Tuple(1u8, 2u32, 3.5f32).fold(10.0, >|x, y: T| x + y.into()); +assert_eq!(b, 16.5f64); +``` + +A fully working example of this code (with manually implemented closures) can be found [here](https://play.rust-lang.org/?gist=ea867336945253752e31873fc752ec06&version=nightly&backtrace=0). + +# Detailed design +[design]: #detailed-design + +## Syntax + +There are two ways to specify generic bounds on closures: + +```rust +|x: T| println!("{:?}", x); + +|x: T| where T: Debug { + println!("{:?}", x); +} +``` + +When using the `where` syntax, the braces around the closure body are mandatory. + +## Implementation + +The generated closure type will have generic implementations of `Fn`, `FnMut` and `FnOnce` with the provided type bounds. This is similar to the way closures currently have generic implementations over lifetimes. + +# Drawbacks +[drawbacks]: #drawbacks + +None + +# Alternatives +[alternatives]: #alternatives + +None + +# Unresolved questions +[unresolved]: #unresolved-questions + +None From 76d3b2aba962d1b3c46c823a4f09776079974aee Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Thu, 16 Jun 2016 05:43:53 +0100 Subject: [PATCH 2/5] Add notes on move closures --- text/0000-generic-closures.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/text/0000-generic-closures.md b/text/0000-generic-closures.md index 86fe370908c..b6e7fc149fd 100644 --- a/text/0000-generic-closures.md +++ b/text/0000-generic-closures.md @@ -60,6 +60,16 @@ There are two ways to specify generic bounds on closures: When using the `where` syntax, the braces around the closure body are mandatory. +If the `move` keyword is used then it must appear before the generic parameter list: + +```rust +move |x: T| println!("{:?}", x); + +move |x: T| where T: Debug { + println!("{:?}", x); +} +``` + ## Implementation The generated closure type will have generic implementations of `Fn`, `FnMut` and `FnOnce` with the provided type bounds. This is similar to the way closures currently have generic implementations over lifetimes. @@ -77,4 +87,4 @@ None # Unresolved questions [unresolved]: #unresolved-questions -None +What are the syntax interactions of `move` generic closures with the proposed `&move` reference type? From 39a89aaf946c2a859f21bdccca9f281605d519a7 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Thu, 23 Jun 2016 01:47:02 +0100 Subject: [PATCH 3/5] Add some drawbacks and alternatives --- text/0000-generic-closures.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/text/0000-generic-closures.md b/text/0000-generic-closures.md index b6e7fc149fd..eeb69a05470 100644 --- a/text/0000-generic-closures.md +++ b/text/0000-generic-closures.md @@ -77,14 +77,26 @@ The generated closure type will have generic implementations of `Fn`, `FnMut` an # Drawbacks [drawbacks]: #drawbacks -None +Increased language complexity. # Alternatives [alternatives]: #alternatives -None +If the given syntax is determined to be ambiguous, this one can be used instead: + +```rust +for|x: T| println!("{:?}", x); + +for|x: T| where T: Debug { + println!("{:?}", x); +} +``` + +We could just not add this, however it would make generic operations on tuples less ergonomic. This feature is going to be even more useful when variadic generics are added in the future. # Unresolved questions [unresolved]: #unresolved-questions What are the syntax interactions of `move` generic closures with the proposed `&move` reference type? + +Is the syntax in this RFC ambiguous for the parser? From 5129c4e47c680b4de615c67af1b94e427b982b99 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Fri, 24 Jun 2016 01:31:59 +0100 Subject: [PATCH 4/5] Specify that all generic parameters must be used in the closure argument list --- text/0000-generic-closures.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/text/0000-generic-closures.md b/text/0000-generic-closures.md index eeb69a05470..8879526e6c1 100644 --- a/text/0000-generic-closures.md +++ b/text/0000-generic-closures.md @@ -70,6 +70,8 @@ move |x: T| where T: Debug { } ``` +All generic parameters must be used in the closure argument list. This is necessary to ensure that the closure can implement all the required `Fn` traits. + ## Implementation The generated closure type will have generic implementations of `Fn`, `FnMut` and `FnOnce` with the provided type bounds. This is similar to the way closures currently have generic implementations over lifetimes. From cbbe44778075a051713f2b7e3f0de511a7375531 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Wed, 29 Jun 2016 15:17:48 +0200 Subject: [PATCH 5/5] Make it clean how generic closures are implemented --- text/0000-generic-closures.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/text/0000-generic-closures.md b/text/0000-generic-closures.md index 8879526e6c1..c808b9e7a03 100644 --- a/text/0000-generic-closures.md +++ b/text/0000-generic-closures.md @@ -76,6 +76,23 @@ All generic parameters must be used in the closure argument list. This is necess The generated closure type will have generic implementations of `Fn`, `FnMut` and `FnOnce` with the provided type bounds. This is similar to the way closures currently have generic implementations over lifetimes. +The closure itself still only has a single type, but it has a generic implementation of the `Fn` traits. Example: + +```rust +|x: T| println!("{:?}", x); + +// Is expanded to: +struct Closure; +impl FnOnce<(T,)> for Closure { + type Output = (); + extern "rust-call" fn call_once(self, args: (T,)) { + println!("{:?}", x); + } +} +``` + +This implementation means that a closure may not be generic over its return type only (unless that type is also used in the argument list), since that would not result in a valid impl. + # Drawbacks [drawbacks]: #drawbacks