Skip to content
Select theme

How to deduplicate a Vec

Short answer: Vec::dedup removes only consecutive duplicates — the same value sitting directly next to itself. On an unsorted Vec it removes almost nothing. When any order will do, sort first and then dedup; when the order matters, keep the first occurrence of each value with a HashSet and retain.

From a fresh project (cargo new dedup-demo), put this in src/main.rs and run cargo run:

[1, 2, 3, 2]
len 4 capacity 7

One pass, no allocation: each element is dropped when it equals its predecessor, and the elements past the new length stay allocated — that’s why len falls to 4 while capacity is still 7. Only runs of equal neighbours shrink, so the trailing 2 survives its distant cousin.

Now the trap. The same call on an unsorted Vec does nothing at all:

[1, 2, 1]

The two 1s never sit next to each other, so neither is “a duplicate of its neighbour”. Nothing sorts for you; dedup only looks sideways.

If any order will do, sort and then dedup. Replace the contents of src/main.rs with:

[1, 2, 3]

Sorting brings the equal values together, which turns “remove the repeats” into “remove the neighbours”. sort_unstable works too — the duplicates here are identical values, so which one survives doesn’t matter. The order that comes back is sorted order, not input order.

When the order matters, drop the later duplicates instead:

["apple", "pear", "fig"]

HashSet::insert returns true when the value is new, so retain keeps each value the first time and drops it after. This needs T: Hash + Eq rather than PartialEq, the values live in a second collection of their own, and every element is cloned once on the way through. For a Vec of values that are expensive to hash, sorting in place can be the cheaper trade.

dedup compiles for f64, but PartialEq for floats says NaN is not equal to itself:

[1.0, NaN, NaN, 2.0]

Consecutive NaNs survive, because “equal to the previous element” is false for NaN. That same contract is why f64 can’t be a HashMap key — a later article in this chapter works through it.

And dedup needs T: PartialEq, so a struct that doesn’t implement it doesn’t compile at all:

error[E0599]: the method `dedup` exists for struct `Vec<Person>`, but its trait bounds were not satisfied
--> src/main.rs:7:12
|
1 | struct Person {
| ------------- doesn't satisfy `Person: PartialEq`
...
7 | people.dedup();
| ^^^^^
|
= note: the following trait bounds were not satisfied:
`Person: PartialEq`
help: consider annotating `Person` with `#[derive(PartialEq)]`
|
1 + #[derive(PartialEq)]
2 | struct Person {
|
For more information about this error, try `rustc --explain E0599`.

The compiler names the missing trait and its own fix. Or skip the derive and compare a field: people.dedup_by_key(|p| p.id) drops adjacent entries with the same id.

Next in this chapter: how to find an element in a Vec.