-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Implement Fn traits for Rc and Arc #71570
Conversation
(rust_highfive has picked a reviewer for you, use r? to override) |
r? @dtolnay for libs-team acceptance of stable surface area expansion cc @eddyb -- do we need to do something special here to not run into the problems encountered here: #68304? I would also like to see some tests to make sure this is working. Regardless we'll only be able to support |
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.
Is there a compelling use case for this? I would prefer not to do this "just because".
Note that function calls do deref coercion so Rc and Arc are already callable as if they had a Fn impl.
fn main() {
std::rc::Rc::new(|| {})();
}
The use case is when you want to pass an The initial reason motivated me was that I have like three widgets in my UI (using gtk-rs) sharing the same closure, and that closure bundles a bunch things so I figured it makes more sense to have the closure itself let update_something = Rc::new(move || {
// ...
});
toggle1.connect_toggled({
let update_something = update_something.clone();
move |_| update_something()
});
toggle2.connect_toggled({
let update_something = update_something.clone();
move |_| update_something()
});
toggle3.connect_toggled({
let update_something = update_something.clone();
move |_| update_something()
}); however, if let update_something = Rc::new(move |_| {
// ...
});
toggle1.connect_toggled(update_something.clone());
toggle2.connect_toggled(update_something.clone());
toggle3.connect_toggled(update_something.clone()); |
|
@Mark-Simulacrum this is harmless because you're not moving potentially-unsized values. |
@dtolnay WDYT about #71570 (comment)? If you and the lib team would be happy with moving forward on this, I can add some tests for it. |
I am on board with adding the impls. Could you add some basic test coverage? It's too easy for this kind of impl to become accidentally infinite recursive in a refactor. |
Yep, done! Hopefully it's enough to catch this kind of issues. I actually tried to make one of the impl recursive with |
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.
Thank you, looks good.
@rust-lang/libs: impl<A, F: Fn<A> + ?Sized> FnOnce<A> for Rc<F> {...}
impl<A, F: Fn<A> + ?Sized> FnMut<A> for Rc<F> {...}
impl<A, F: Fn<A> + ?Sized> Fn<A> for Rc<F> {...}
impl<A, F: Fn<A> + ?Sized> FnOnce<A> for Arc<F> {...}
impl<A, F: Fn<A> + ?Sized> FnMut<A> for Arc<F> {...}
impl<A, F: Fn<A> + ?Sized> Fn<A> for Arc<F> {...} The trait bounds on the above new impls match the impls for &F that have existed since 1.0.0: impl<A, F: Fn<A> + ?Sized> FnOnce<A> for &F {...}
impl<A, F: Fn<A> + ?Sized> FnMut<A> for &F {...}
impl<A, F: Fn<A> + ?Sized> Fn<A> for &F {...} |
Team member @dtolnay has proposed to merge this. The next step is review by the rest of the tagged team members: Concerns:
Once a majority of reviewers approve (and at most 2 approvals are outstanding), 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. |
The |
|
use std::rc::Rc;
trait LocalTrait {}
impl<F> LocalTrait for Rc<F> where F: Fn() {}
impl<F> LocalTrait for F where F: Fn() {} |
@nbdd0121 Ah, indeed, I didn't think about that angle. |
@rfcbot concern This is a breaking change, and definitely not an "allowed" breaking change |
That's a pretty strong reason not to do this. Is this a case where we would trust Crater on whether this kind of "impl for { F, Rc<F> } where F: Fn" or anything similar is a thing people do, or better to abandon? |
I think I prefer not merging this due to the breaking change. I don't think the advantages of these impls are great enough to violate the promise we explicitly (by annotating |
IIUC, the reason Because of the inconsistency, I think this can be considered an oversight when implementing |
Crater can never test all Rust code in the world. It only tests a sample, with significant selection bias. So even with a perfectly green Crater run, knowingly landing a breaking change has non-zero risk. For breaking changes considered "minor", that risk is to be balanced against the benefits brought by the change. Here the use case (#71570 (comment)) feels rather niche to me. The work-around is not that terrible, and its repetition can be reduced: let update_something = Rc::new(move || {
// ...
});
let clone_update_something = || {
let update_something = update_something.clone();
move |_| update_something()
};
toggle1.connect_toggled(clone_update_something());
toggle2.connect_toggled(clone_update_something());
toggle3.connect_toggled(clone_update_something()); However even if we judged the benefit worth the estimated risk, we have an accepted RFC that categorizes this kind of change as "major": https://rust-lang.github.io/rfcs/1105-api-evolution.html#major-change-implementing-any-fundamental-trait , and states that a major breaking change (that cannot be made edition-specific) requires incrementing the major component of the version number. (Personally I’d prefer Rust 2.0 to never happen.) |
I agree with that. @rfcbot fcp cancel |
@dtolnay proposal cancelled. |
Thanks anyway @upsuper! |
Having forgotten about this PR, I was playing with such impls again and found this real-world conflict: error[E0119]: conflicting implementations of trait `fmt::writer::MakeWriter<'_>` for type `std::sync::Arc<_>`
--> /home/jistone/.cargo/registry/src/github.com-1ecc6299db9ec823/tracing-subscriber-0.3.3/src/fmt/writer.rs:686:1
|
674 | impl<'a, F, W> MakeWriter<'a> for F
| ----------------------------------- first implementation here
...
686 | impl<'a, W> MakeWriter<'a> for Arc<W>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `std::sync::Arc<_>`
For more information about this error, try `rustc --explain E0119`.
error: could not compile `tracing-subscriber` due to previous error |
Given that
Box
implementsFn
traits, I think it makes sense forRc
andArc
to do so as well. But since inner ofRc
andArc
is shared, apparently they can only be invoked whenFn
is satisfied.WDYT?
I can create a tracking issue for it if the team is fine with this new
impl
.