You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Vec::retain could (should?) give &mut T to the closure, not just &T #25477
However, looking at its implementation, I don’t see a reason why the &T part couldn’t be &mut T, allowing in-place mutation of retained items. This would make the method more useful. The only downside that I can see is that the name does not reflect this new capability of the method. I believe this change would be backward-compatible.
This came up at today’s meetup where someone have a Vec<Item> and wanted (but didn’t know how) to iterate the vector, decreasing some quantity inside Item but also removing items from the vector when the quantity reaches zero.
Speaking in context of rust-lang/rfcs#1105 this is a breaking change, but probably qualifies as a minor change. It might be interesting to pull it into the discussion as an example.
Oh, right. I was thinking of using this method with a closure with inferred parameter types, but it is indeed breaking if you have type annotations.
From the RFC:
it must always be possible to locally fix the problem through some kind of disambiguation that could have been done in advance (by using more explicit forms)
This case seems unusual since using the more implicit form is the change you could do in advance, but I suppose it qualifies.
I am +1 for this change in principle, but slightly worried about potential breakage. It's probably worth prototyping and testing out with @brson's crater tool.
If this causes breakage, an alternative would be to add a separate retain_mut method. The downside is that we then end up with two very similar methods, which might become confusing.
Changing the parameter to &mut breaks uses of retain with closures of the form |&x| {...}. There are a few uses of this in rustc, so this pattern might be used in user code as well.
It's interesting in general that we could choose to give out &mut instead of &, everywhere there is exclusive access even if you'd typically not mutate. Another example is Iterator::filter.
Sounds like the resolution shall be a separate API @gankro, retain_mut() which can be developed outside libstd.
Another case is BTree::iter_mut for the keys. I think in most of these cases, mutable access is a bit "strange" to have intuitively (and in the case of BTree, a footgun), but I think that's because we're not super used to thinking of &mut and & in terms of their shared-ness, but rather in terms of mutability (because that's often the most natural perspective IMO).
retain_mut is simple to lift out (retain uses only public api). I've put retain_mut into the crate odds.
I've suggested we have some kind of gathering of more-than-libstd collections API in a crate in contain-rs, no other interested people have said anything in that discussion yet.
Given that F: Fn(.., &T, ..) can be called anywhere that F: Fn(.., &mut T, ..) can, I wonder if it would be possible to make them coerce? Then the problems with this change would presumably be reduced to just type-inferal issues. (Obviously that would need an RFC.)
Triage: as time goes on here, I don't think that we can make this change, as the breakage is much more significant than in the few days post 1.0. I'm going to give it a close.
So what is planned to remove elements based on predicates and mutate the elements? #34265 has been closed in favor of this issue, but this issue has been closed too.
Also, drain_filter is an iterator, so you have to drive it to completion for it to have any effect. retain is more eager, so it's more ergonomic for that case:
let mut v = vec![1, 2, 3, 4, 5, 6];
v.retain(|x| x % 2 == 0);
println!("{:?}", v);
let mut v = vec![1, 2, 3, 4, 5, 6];
v.drain_filter(|x| *x % 2 != 0).count();
println!("{:?}", v);
Existing drain_filter impl does not need to be driven explicitly -- it is driven in drop, too. It makes panic behaviour interesting but it is similar to .drain().
I've read all the comments, but still confused about what to do, that's not good! Ideally there would be a pinned comment or something with current best solution. I see that there are two crates odds and retain_mut that implement this... Are they best solution?
@Boscopdrain_filter does something different... or it's documentation isn't as clear as retain's. drain_filter creates an iterator that would then need to be consumed ??for anything to happen??
Looking into it, that just leads to another question... Not a solution that's easily grasped. If drain_filter was supposed to be retain_mut it's not clear how.
Activity
bluss commentedon May 16, 2015
Speaking in context of rust-lang/rfcs#1105 this is a breaking change, but probably qualifies as a minor change. It might be interesting to pull it into the discussion as an example.
For the idea itself, it sounds sound and useful.
SimonSapin commentedon May 17, 2015
Oh, right. I was thinking of using this method with a closure with inferred parameter types, but it is indeed breaking if you have type annotations.
From the RFC:
This case seems unusual since using the more implicit form is the change you could do in advance, but I suppose it qualifies.
aturon commentedon May 28, 2015
I am +1 for this change in principle, but slightly worried about potential breakage. It's probably worth prototyping and testing out with @brson's crater tool.
Amanieu commentedon Nov 6, 2015
If this causes breakage, an alternative would be to add a separate
retain_mutmethod. The downside is that we then end up with two very similar methods, which might become confusing.Amanieu commentedon Nov 6, 2015
Changing the parameter to &mut breaks uses of
retainwith closures of the form|&x| {...}. There are a few uses of this in rustc, so this pattern might be used in user code as well.One example in rustc:
Gankra commentedon Nov 6, 2015
I'm not a huge fan of doing this. We made a bad call. That sucks, but this is also why we expose the guts of Vec.
bluss commentedon Nov 7, 2015
It's interesting in general that we could choose to give out
&mutinstead of&, everywhere there is exclusive access even if you'd typically not mutate. Another example isIterator::filter.Sounds like the resolution shall be a separate API @gankro,
retain_mut()which can be developed outside libstd.Gankra commentedon Nov 7, 2015
Another case is
BTree::iter_mutfor the keys. I think in most of these cases, mutable access is a bit "strange" to have intuitively (and in the case of BTree, a footgun), but I think that's because we're not super used to thinking of&mutand&in terms of their shared-ness, but rather in terms of mutability (because that's often the most natural perspective IMO).bluss commentedon Nov 7, 2015
retain_mut is simple to lift out (retain uses only public api). I've put retain_mut into the crate odds.
I've suggested we have some kind of gathering of more-than-libstd collections API in a crate in contain-rs, no other interested people have said anything in that discussion yet.
wthrowe commentedon Nov 7, 2015
Given that
F: Fn(.., &T, ..)can be called anywhere thatF: Fn(.., &mut T, ..)can, I wonder if it would be possible to make them coerce? Then the problems with this change would presumably be reduced to just type-inferal issues. (Obviously that would need an RFC.)Amanieu commentedon Nov 7, 2015
I wrote an RFC for
retain_mut: rust-lang/rfcs/pull/1353arthurprs commentedon Nov 10, 2015
I'm ok with this change.
steveklabnik commentedon Mar 8, 2017
Triage: as time goes on here, I don't think that we can make this change, as the breakage is much more significant than in the few days post 1.0. I'm going to give it a close.
real-felix commentedon Sep 21, 2017
So what is planned to remove elements based on predicates and mutate the elements? #34265 has been closed in favor of this issue, but this issue has been closed too.
bluss commentedon Sep 21, 2017
Drain filter is going to cover that.
real-felix commentedon Sep 22, 2017
If I do understand,
retainwill be useless? Each call toretaincan be replaced with a call todrain_filter.bluss commentedon Sep 22, 2017
Well, that's up to your judgement. Existing retain usages will continue to work, which has a value.
shepmaster commentedon Sep 22, 2017
Also,
drain_filteris an iterator, so you have to drive it to completion for it to have any effect.retainis more eager, so it's more ergonomic for that case:bluss commentedon Sep 22, 2017
Existing drain_filter impl does not need to be driven explicitly -- it is driven in drop, too. It makes panic behaviour interesting but it is similar to .drain().
shepmaster commentedon Sep 22, 2017
Ah, I guess that makes sense.
upsuper commentedon Nov 28, 2018
FYI, I just published a crate
retain_mutwhich implements the idea here.cheako commentedon Jun 12, 2020
I've read all the comments, but still confused about what to do, that's not good! Ideally there would be a pinned comment or something with current best solution. I see that there are two crates
oddsandretain_mutthat implement this... Are they best solution?Boscop commentedon Jun 13, 2020
@cheako You can also use
drain_filterbut you have to negate the closure's return value.cheako commentedon Jun 13, 2020
@Boscop
drain_filterdoes something different... or it's documentation isn't as clear asretain's.drain_filtercreates an iterator that would then need to be consumed ??for anything to happen??Looking into it, that just leads to another question... Not a solution that's easily grasped. If
drain_filterwas supposed to beretain_mutit's not clear how.Merge #85
Ten0 commentedon Jan 28, 2022
For reference,
retain_mutis currently being added to the standard library: #90829Merge RoaringBitmap#85