APT37
September 11, 2026, 7:20am
1
Just thought it was odd that there was no retain equivalent that uses swap_remove under the hood. Why not add something like this?
fn retain_unordered<T, F>(vec: &mut Vec<T>, mut keep: F)
where
F: FnMut(&T) -> bool,
{
let mut i = 0;
while i < vec.len() {
if keep(&vec[i]) {
i += 1;
} else {
vec.swap_remove(i);
}
}
}
Thank you for your time.
I'd be curious if that has significant performance benefit.. and if so, how much.
Of course the precise results could also depend on the complexity of the predicate's logic, size of the item datatype, proportion (and pattern?) of values to be removed... etc. Nonetheless have you already benchmarked this? (Or if not, could you maybe try that?)
From an asymptotic point of view, the run time is going to be basically linear w. r. t. the number of elements either way (because you ultimately need to visit every single element). Notably retain is much much more efficient already than what a loop with calls to remove would be. (The latter would actually use time quadratic w. r. t. the number rof elements in most cases.)
2 Likes
APT37
September 11, 2026, 9:50am
3
Sorry, I did not perform any benchmaks.
Using this vs. the standard retain implementation would not lead to any useful results either way, since that uses a way more efficent method to achieve the compaction (which I only found out after posting).
The question was ultimately unqualified, I did not know that the low level implementation is as optimized as it is - instead imagining something similar to the same simplistic logic as in my proposed example.
1 Like
pitaj
September 11, 2026, 7:11pm
4
For cases where you're keeping most elements, I could see the unordered version being significantly faster.
1 Like
pitaj
October 11, 2026, 2:57pm
5
Huh. There was already an accepted ACP for this
opened 06:43PM - 03 Oct 23 UTC
closed 04:11PM - 17 Oct 23 UTC
api-change-proposal
ACP-accepted
# Proposal
## Problem statement
Efficiently removing elements of a vec w… ithout preserving order is currently not supported by the standard library.
| | single element | multi element |
| -------- | -------------- | ----------------- |
| Preserves ordering | `remove` | `retain` |
| Does not preserve ordering | `swap_remove` | Does not exist |
## Motivating examples or use cases
While modeling a dense graph using `Vec<Vec<usize>>` (so called adjacency list) where each `Vec<usize>` represents adjacent nodes, during the removal of nodes, I needed a fast way to remove adjancency list entries (typically very few compared to the total size of the graph) without preserving order in the adjacency list.
## Solution sketch
A docs level description of the feature could look like this (modified from `Vec::retain`) :
> Retains only the elements specified by the predicate _without preserving the original order_.
>
> In other words, remove all elements `e` for which `f(&e)` returns `false`.
> This method operates in place, visiting each element exactly once in
> *an unspecified order* and *does not* preserves the order of the retained elements.
>
> # Examples
>
> ```
> let mut vec = vec![1, 2, 3, 4];
> vec.swap_retain(|&x| x % 2 == 0);
> assert!(vec.contains(2));
> assert!(vec.contains(4));
> ```
>
> Because the elements are visited exactly once *but not in the original order*,
> external state may be *of limited use* when deciding which elements to keep.
>
As for the method name, the two proposals that came up on Zulip were `swap_retain` and `retain_unstable`.
The former creates a nice symmetry in the table up top, but promises more than we might be willing to guarantee.
`retain_unstable` uses the word unstable in the way that sorting algorithms use it, but unstable has a semantic overload in Rust which might make it undesirable.
@pitaj has pointed out that the insertion methods in this space (iterator vs single element, in place vs at the end) are named `insert`, `splice`, `push`, `extend`, so there is existing precedent for not having a consistent naming scheme.
## Alternatives
[This playground link](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=38113854943acb3cac1257798088a1df) contains two implementations of a potential `swap_retain` method. (I originally named them `swap_retain_mut` due to `retain` having two different methods, but @cuviper pointed out that this is a historical accident. This ACP only proposes a single method.)
The second implementation was done by @quaternic and already performs a lot better than my previous attempts, but it still might move a single element multiple times, so there is still some performance to be gained.
I'm unaware of a crate the extends the standard containers with additional methods, but if there is such a crate it might make sense to move the implementation there.
## Links and related work
[I created this Zulip stream before the ACP](https://rust-lang.zulipchat.com/#narrow/stream/327149-t-libs-api.2Fapi-changes/topic/.E2.9C.94.20swap_retain.20for.20Vec/near/394377681)
## What happens now?
This issue is part of the libs-api team [API change proposal process]. Once this issue is filed the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
[API change proposal process]: https://std-dev-guide.rust-lang.org/feature-lifecycle/api-change-proposals.html
## Possible responses
The libs team may respond in various different ways. First, the team will consider the *problem* (this doesn't require any concrete solution or alternatives to have been proposed):
- We think this problem seems worth solving, and the standard library might be the right place to solve it.
- We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
- We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
- We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.
2 Likes
APT37
October 11, 2026, 3:10pm
6
Oh, that's neat. All we have to do is to wait for it to actually be implemented.
Thank you for letting me know!
APT37
October 12, 2026, 2:01pm
9
Guess I'll have to search harder next time. Can someone close this?
The PR was submitted yesterday, possibly as a reaction to this very thread You should be able to mark the thread as resolved by selecting some message as "the answer". Or was that only on urlo?
2 Likes
APT37
October 12, 2026, 4:55pm
11
Wow, I didn't actually check the PRs date. Guess it was a good idea to ask about this after all.
The only option seems to wait for the thread to be inactive for a year, though I'll stop 'watching'.
We only have the topic-resolve feature enabled only on users.rust-lang.org , not here, so you can't find that; sorry for any confusion.
I will shorten the auto-close interval so this topic is closed sooner.
2 Likes