New lint: Suggests using Cell<T: Copy> instead of RefCell<T: Copy> - #17522
New lint: Suggests using Cell<T: Copy> instead of RefCell<T: Copy>#17522qdot3 wants to merge 11 commits into
Cell<T: Copy> instead of RefCell<T: Copy>#17522Conversation
|
Thanks for the pull request, and welcome! You should hear from one of our reviewers after this PR gets at least 2 reviews from the community. Please see the contribution instructions for more information. Namely, in order to ensure the minimum review times lag, PR authors and assigned reviewers should ensure that the review label (
|
This comment has been minimized.
This comment has been minimized.
| // Because this may be required to be `RefCell` | ||
| let mut app = Applicability::MaybeIncorrect; |
There was a problem hiding this comment.
Seems like a bit of an unclear comment. Can you clarify this comment?
There was a problem hiding this comment.
Fixed:
// This can be a false positive, for example, when passed to a function
// that requires `RefCell`.
There was a problem hiding this comment.
It should ideally be handled, but I'm not sure whether it is feasible. I need some time to investigate.
| let mut app = Applicability::MaybeIncorrect; | ||
| let sugg = { | ||
| let (init, _) = snippet_with_context(cx, span, stmt.span.ctxt(), "..", &mut app); | ||
| init.into_owned().replace("RefCell", "Cell") |
There was a problem hiding this comment.
this .replace("RefCell", "Cell") seems suspicious.
How about RefCell<InnerRefCell> where InnerRefCell is a struct with Copy?
There was a problem hiding this comment.
Fixed. I now address HIR directly.
| snippet(cx, init.span, "").contains("RefCell"), | ||
| let_stmt | ||
| .ty | ||
| .is_some_and(|ty| snippet(cx, ty.span, "").contains("RefCell")), |
There was a problem hiding this comment.
Is the blanket String .contains here safe, as in does this have false-positives? Can't we do this via matching instead as in the outer if-let?
There was a problem hiding this comment.
Fixed. I now handle both the type annotation and the initialization expression directly.
| @@ -1,3 +1,4 @@ | |||
| #![allow(clippy::refcell_cell)] | |||
There was a problem hiding this comment.
can this be expect? (also applies to the other instances)
The reason behind this is that this way we "GC" these lints when they don't apply.
There was a problem hiding this comment.
instead of an lint, should this be a change to that lint to not lint in this case?
There was a problem hiding this comment.
Since refcell_cell is a perf lint, I think it should be fired in this case. Using RefCell over Cell here can be a matter of style, and I think the choice between perf and style should be left to users.
There was a problem hiding this comment.
should we not instead add an exeption to those lints to not lint?
They seem a bit conflicting...
| /// ### Why is this bad? | ||
| /// `RefCell` avoids cloning at the cost of additional memory usage and | ||
| /// instructions, which isn't worth it for `Copy` types. | ||
| /// |
There was a problem hiding this comment.
there are some Known problems regarding trait bounds as noted in the tests, can we document them here?
Also, we need to note that while on avarage this is a good idea, there might be better options..
There is no runtime cost to using
Cell<T>, however if one is using it to wrap larger (Copy) structs, it might be worthwhile to instead wrap individual fields inCell<T>since each write is a full copy of the struct
https://manishearth.github.io/blog/2015/05/27/wrapper-types-in-rust-choosing-your-guarantees/
There was a problem hiding this comment.
I documented it and introduced new configuration named max_cheap_copy_size to suppress the lint for large Copy types. The default value is 16, witch is the size of u/i128.
There was a problem hiding this comment.
How did you come up/reason that this should be 16? (It might be right, but I'd like to know how you came up with it) 🤔
Can you add that either as a comment or into the PR description since otherwise it is not very clear.
There was a problem hiding this comment.
To be honest, I chose it simply because it's the minimum size required to represent all primitive integer types. But the word size is better, since it usually fits in a cache line. And since this is a perf lint, performance should take priority over avoiding extra configuration.
This comment has been minimized.
This comment has been minimized.
0246405 to
8cef563
Compare
Implementation of #17380
This lint suggests using
Cellinstead ofRefCellforCopytypes.RefCellavoids cloning, but it introduces additional memory overhead and runtime checks, which are not worthwhile forCopytypes.Scope
letstatements (e.g.,let _ = RefCell::new(1))Out of scope
letstatements