We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ground Rules: Values, Types, Control Flow step 20 of 24
Remove duplicates while preserving order
Return each distinct input value once, in the order of its first appearance. Leave the input unchanged.
pub fn dedup_preserve_order(values: &[i32]) -> Vec<i32>
[1, 2, 1, 3, 2] produces [1, 2, 3]. An empty slice produces an empty
vector, and [7, 7, 7] produces [7]. Negative values and the full i32
range are valid. Do not sort the result: [5, 2, 5, 1] must produce
[5, 2, 1], not [1, 2, 5].
The starter computes the right values, but its parameter and loop trigger
Clippy warnings. Fix those warnings while preserving the behavior. The
&[i32] signature also lets callers pass arrays and slices directly.
A vector stores a sequence that can grow
Vec<i32> owns contiguous storage for its elements. Its length counts the
initialized elements, and its capacity is how many elements it can hold before
it needs to grow. Pushing an element increases the length by one.
A vector keeps a pointer, length and capacity separately from its element
storage. An empty Vec::new() need not allocate. Growth can reallocate its
buffer, possibly moving the elements; do not assume the address stays fixed.
Rust does not specify a fixed growth factor or an exact allocation count.
Vec::with_capacity(n) reserves room for at least n elements, but here the
number of distinct values may be much smaller than the input length.
Accept a slice when you only need the elements
This function only reads its input. A slice provides the operations it needs without requiring the caller to supply a vector. For example, a function taking a slice can read all or part of an array:
fn first(values: &[i32]) -> Option<i32> {
values.first().copied()
}
fn main() {
let values = [8, 3, 8];
assert_eq!(first(&values), Some(8));
assert_eq!(first(&values[1..]), Some(3));
assert_eq!(first(&[]), None);
}
A vector also works through a slice borrow, such as &values or
&values[1..]. Clippy's ptr_arg warning recommends changing this starter's
&Vec<i32> parameter to &[i32]. This is appropriate because the function
does not need vector-specific information such as capacity. It is not a rule
that every function taking a vector reference can always use a slice.
Simplify the loop and condition
The starter indexes values using every integer in 0..values.len().
Replace that with for &value in values: it borrows each element and copies
its i32 value into the loop variable. This addresses needless_range_loop
and avoids managing an index that the algorithm does not otherwise need.
It does not imply that every indexed loop is wrong or that each indexed
access necessarily retains a bounds check after optimization.
Clippy's len_zero warning recommends is_empty() over len() == 0. In
this particular algorithm you can remove the emptiness check entirely:
out.contains(&value) is already false when out is empty. Push the value
only when it is absent from out.
Borrowing and consuming a vector are different
The element type being Copy does not make the vector itself Copy.
This complete example shows three forms of iteration:
fn main() {
let mut values = vec![1, 2, 3];
for value in &values {
assert!(*value > 0); // value is &i32; values stays usable.
}
for value in &mut values {
*value += 10; // value is &mut i32.
}
assert_eq!(values, [11, 12, 13]);
let mut sum = 0;
for value in values {
sum += value; // value is i32; this loop consumes the vector.
}
assert_eq!(sum, 36);
}
After the final loop, using values again would produce a moved-value error.
The visualization illustrates the same ownership rule with an assignment.
Loading visualization…
Why this algorithm preserves order
Scan left to right and append a value only on its first occurrence. The output
doubles as the record of values already seen. Later duplicates leave it alone.
Vec::dedup() alone is different: it removes only consecutive duplicates.
Searching the output with contains takes linear time in its current length,
giving this approach O(n²) worst-case time and O(k) output space for k
distinct values. It is a simple approach for this exercise's small inputs.
For larger inputs, a separate HashSet can track membership while a vector
retains the order, giving expected O(n) total time with extra storage. Returning
the set's iteration order would not meet this task's contract.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.