-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
Copy/Clone Closures #2132
Copy/Clone Closures #2132
Conversation
FWIW I keep seeing people refer to that change as "making What happened was automagic |
Does this include function types and function pointers as well? Currently Clone is manually implemented for:
|
How will this interact with generators (#2033)? |
@kennytm I'd like to think that generators would follow the same rules, but as no formal RFC for generators has been merged, I think that's outside the scope of this RFC. |
This is a pretty straightfoward and very welcome RFC (eliminating a small but painful papercut). I'm going to go ahead and get the wheels turning toward FCP review: @rfcbot fcp merge |
Team member @aturon has proposed to merge this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once these reviewers reach consensus, this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
I remember being sort of happy in the implementation that closures were just never |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
@nikomatsakis Is it because it could be confusing to accidentally make a copy, resume the copy, and not see side effects on the original? (Same as iterators.) Maybe it makes sense to deliberately make generators not Copy, but they should definitely be Clone whenever possible. |
@SimonSapin Hmm. I don't think that issue is specific to generators per se, it applies to any closure that owns mutable state. (After all, at a deep level at least, a generator is just a function.) Example: fn main() {
let mut i = 0;
let mut get_value = move || -> u32 { i += 1; return i; };
println!("{}", get_value()); // prints 1
let mut get_value2 = get_value; // currently errors, but wouldn't with this RFC.
println!("{}", get_value()); // prints 2
println!("{}", get_value2()); // prints 1
} I'm not sure if you consider surprising, or just expected behavior. It certainly behaves the same as any other value would, but it depends on how precise your "mental model" is for what state is owned by the closure versus being referenced from the surrounding stack frame, I suppose. |
@SimonSapin I have an example of this in the RFC-- check out how |
IIRC, this is basically the reason ...ah, in fact, I think we're both remembering the same discussion. |
Considering
Note that this still requires that objects contained in closure are copyable/cloneable. For instance a closure owning Also out of curiosity I consider the behaviour of Here is a reference version to be called with let cell = Cell::new(0);
let print_incr = &|| {
println!("{}", cell.get());
cell.set(cell.get() + 1);
}; And here is #![feature(fn_traits, unboxed_closures)]
use std::cell::Cell;
#[derive(Copy, Clone)]
struct Incrementer<'a> {
cell: &'a Cell<i32>,
}
impl<'a> Fn<()> for Incrementer<'a> {
extern "rust-call" fn call(&self, _args: ()) {
let cell = self.cell;
println!("{}", cell.get());
cell.set(cell.get() + 1)
}
}
fn main() {
let cell = Cell::new(0);
let incrementer = Incrementer { cell: &cell };
fn call_three_times<F: FnMut()>(mut f: F) {
for _ in 0..3 {
f();
}
}
call_three_times(incrementer);
call_three_times(incrementer);
}
// Noise needed for technical reasons
impl<'a> FnMut<()> for Incrementer<'a> {
extern "rust-call" fn call_mut(&mut self, args: ()) {
self.call(args)
}
}
impl<'a> FnOnce<()> for Incrementer<'a> {
type Output = ();
extern "rust-call" fn call_once(self, args: ()) {
self.call(args)
}
} |
text/0000-copy-closures.md
Outdated
[guide-level-explanation]: #guide-level-explanation | ||
|
||
If a non-`move` closure doesn't mutate captured variables, | ||
then it is `Copy` and `Clone`: |
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.
It's a nit pick, but -- strictly speaking -- this is not always true. A non-move closure can take ownership of values that it (in turn) moves, and hence might not be copy or clone (if those values are not copy or clone).
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.
(Notably, those closures would be FnOnce
.)
text/0000-copy-closures.md
Outdated
# Unresolved questions | ||
[unresolved]: #unresolved-questions | ||
|
||
- How can we provide high-quality, tailored error messages to indicate why a |
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.
This is relatively easy. I don't think it has to be considered an unresolved question. However, I do think it would be valuable to give an example of the sort of message you would expect. =)
I think I would propose separately feature-gating "Copy" and "Clone" on closures -- in particular, I don't think there is much objection to closures being clone, but it'd be interesting to see if copy closures cause any kinds of problems. But really I think a lot of these concerns come back to the desire to have a kind of "Copy" lint that warns you when you are implicitly copying values that you may not want to implicitly copy. (Which could then be silenced by invoking |
The final comment period is now complete. |
This RFC has been merged! Tracking issue. |
Implement `Copy`/`Clone` for closures Implement RFC [#2132](rust-lang/rfcs#2132) (tracking issue: #44490). NB: I'm not totally sure about the whole feature gates thing, that's my first PR of this kind...
Forgive me if I'm just totally wrong here, but wouldn't this break boxed closures? I've had issues before with not being able to box a trait because it implemented |
@dead10ck Do you have any code the reproduce the error? ("not being able to box a trait because it implemented Clone") |
@dead10ck Clone isn't object safe itself, but it's fine to have a type that implements Clone and some object safe trait like Fn turn into a Fn trait object. |
@kennytm yeah, e.g. https://play.rust-lang.org/?gist=cc5e61ee6a37a21dae1e145911bb620f&version=stable @sfackler right, but if I'm understanding this RFC proposal, wouldn't it make |
@dead10ck Ok, the problem in your code is that // Closures which reference data mutably are not `Copy`/`Clone`:
let mut x = 0;
let incr_x = || x += 1;
call(incr_x);
call(incr_x); // ERROR: `incr_x` moved in the call above. |
@kennytm I see, so it's not a blanket // Many `Iterator` combinators are now `Copy`/`Clone`:
let x = (1..100).map(|x| x * 5);
let _ = x.map(|x| x - 3); // moves `x` by `Copy`ing
let _ = x.chain(y); // moves `x` again
let _ = x.cycle(); // `.cycle()` is only possible when `Self: Clone` |
@dead10ck It is fine to box and return it as |
@kennytm I see, thank you! Sorry for the confusion. I'm still pretty inexperienced with Rust. |
I'm pretty OK with this one; finding the graph groups of 2000 lines of connection input in less than half a second seems like a decent job to me. I hadn't expected to need to factor out map_connections_generic for part 2, but I can't complain about the result; I kind of like how it came out. I do wish that closures implemented Copy/Clone appropriately, though: rust-lang/rfcs#2132 $ cargo build --release && time target/release/day12 && wc -l input.txt Compiling day12 v0.1.0 (file:///mnt/d/Users/coriolinus/Documents/Projects/adventofcode.com/adventofcode-2017/day12) Finished release [optimized] target(s) in 4.92 secs real 0m0.397s user 0m0.375s sys 0m0.000s 2000 input.txt
Implement
Clone
andCopy
for closures where possible:Rendered
Note: this RFC was made possible by the recent change to make
Clone
a lang item.