Introduction: Why Finding an Index in Rust is Not Just "Find and Return"?
\n\nEvery developer transitioning to Rust from Python or JavaScript will sooner or later ask the question: "How do I find the index of an element in an array or vector here?" In Python, this is done with one line list.index(element), in JavaScript — array.indexOf(element). But Rust, like a true perfectionist, offers not one, but several ways, each with its own nuances of performance and safety.
Let's figure out how to find the index of an element in Rust collections (arrays, vectors, and slices) so that the code is not only correct but also fast. After all, in systems programming, every nanosecond counts!
\n\nThe Basics: Main Methods for Finding an Index in Rust
\n\nIn the Rust standard library, there are two main "players" for finding an index: position() and rposition(). They are available for any iterators, and therefore for arrays, vectors, and slices.
The position() Method: Left-to-Right Search
\n\nposition() takes a closure and returns Option<usize> — the index of the first element satisfying the condition, or None if such an element is not found.
fn main() {\n let numbers = vec![10, 20, 30, 40, 50];\n \n // Find the first occurrence of the number 30\n let index = numbers.iter().position(|&x| x == 30);\n \n match index {\n Some(i) => println!("Index of number 30: {}", i), // Output: 2\n None => println!("Element not found"),\n }\n}\n\n\nNote: we call iter() before position(). This is important because position() is a method of the Iterator trait, not of the collection itself.
The rposition() Method: Right-to-Left Search
\n\nIf you need the last index of an element's occurrence, use rposition(). It works similarly but traverses the collection from the end:
fn main() {\n let data = vec!['a', 'b', 'c', 'b', 'a'];\n \n // Find the last occurrence of 'b'\n let last_b = data.iter().rposition(|&c| c == 'b');\n \n println!("Last 'b' at index: {:?}", last_b); // Some(3)\n}\n\n\nImportant limitation: rposition() only works with collections that support bidirectional traversal (the DoubleEndedIterator trait). Fortunately, all standard Rust collections satisfy this requirement.
Performance Comparison: position() vs Manual Iteration
\n\nMany beginners write loops manually, thinking it's faster. Let's test this assumption in practice.
\n\nConsider two approaches to finding the index of an element in a vector of 1 million integers:
\n\nuse std::time::Instant;\n\nfn main() {\n let large_vec: Vec<i32> = (0..1_000_000).collect();\n let target = 999_999;\n \n // Method 1: position()\n let start = Instant::now();\n let pos1 = large_vec.iter().position(|&x| x == target);\n let duration1 = start.elapsed();\n println!("position(): {:?}, time: {:?}", pos1, duration1);\n \n // Method 2: manual loop\n let start = Instant::now();\n let mut pos2 = None;\n for (i, &val) in large_vec.iter().enumerate() {\n if val == target {\n pos2 = Some(i);\n break;\n }\n }\n let duration2 = start.elapsed();\n println!("Manual loop: {:?}, time: {:?}", pos2, duration2);\n}\n\n\nResults on modern processors usually show that position() performs as fast as a manual loop, and sometimes faster due to internal optimizations by the Rust compiler (LLVM). Conclusion: use position() — it is idiomatic, safe, and does not sacrifice speed.
Binary Search: When Data is Sorted
\n\nIf your collection is sorted, you should use binary_se