Question
How can I check whether a value exists in a Rust vector (Vec)? For example, given this vector:
let n = vec!["-i", "mmmm"];
// How can I print "yes" if "-i" is present,
// and "no" otherwise?
Is there a built-in method for this, or do I need to loop through the vector and compare each item manually?
Short Answer
You will learn how to test whether a Vec contains a value in Rust. The main approach is Vec::contains, while iterator methods such as iter().any(...) are useful when you need custom matching rules.
Concept
A Rust vector, written as Vec<T>, stores an ordered collection of values of one type. To determine whether a particular value is stored in that collection, use the built-in contains method.
let n = vec!["-i", "mmmm"];
if n.contains(&"-i") {
println!("yes");
} else {
println!("no");
}
contains checks every element until it finds an equal value. It returns a boolean:
truewhen the value is presentfalsewhen the value is not present
The argument has an & because contains borrows the value being searched for rather than taking ownership of it. This is important in Rust: checking a vector should not consume or destroy either the vector or the search value.
contains works for element types that can be compared for equality, expressed by Rust's PartialEq trait. Common types such as numbers, bool, String, and string slices (&str) support this.
Mental Model
Think of a vector as a row of labeled storage boxes. contains is like asking an assistant: “Is there a box holding this exact item?”
The assistant starts at the first box and compares its contents with the item you requested. If a match is found, the answer is true. If every box has been checked without a match, the answer is false.
You do not have to write that search yourself when an exact equality check is enough—contains already performs it.
Syntax and Examples
Use contains with a reference to the value you want to find:
let values = vec![10, 20, 30];
if values.contains(&20) {
println!("20 was found");
}
For a vector of string slices:
let flags = vec!["-i", "--verbose", "--help"];
let has_ignore_case_flag = flags.contains(&"-i");
println!("{has_ignore_case_flag}"); // true
For a vector of owned String values:
let users = vec![String::from("Ada"), String::from("Grace")];
let target = String::from("Ada");
if users.contains(&target) {
println!("User exists");
}
Use iter().any(...) when the condition is more than exact equality:
let words = vec!["apple", "banana", "pear"];
let has_long_word = words.iter().any(|word| word.len() > 5);
println!("{has_long_word}"); // true, because "banana" has 6 letters
any calls the closure for elements until the closure returns true.
Step by Step Execution
Consider this code:
let n = vec!["-i", "mmmm"];
let found = n.contains(&"-i");
if found {
println!("yes");
} else {
println!("no");
}
Execution proceeds as follows:
vec!["-i", "mmmm"]creates aVec<&str>containing two string slices.n.contains(&"-i")searches for an element equal to"-i".containscompares the first element,"-i", with the target.- They are equal, so
containsreturnstrueimmediately. It does not need to inspect"mmmm". foundstorestrue.- The
ifcondition is true, so the program printsyes.
If the target were "--debug", Rust would compare it with both entries, find no match, return false, and print no.
Real World Use Cases
Vector membership checks are common when a program has a small or ordered list of values:
- Command-line flags: Check whether arguments include
"--verbose"or"-i". - Permissions: Check whether a user's role list contains
"admin". - Validation: Confirm that a submitted category is one of the allowed categories.
- Feature toggles: Check whether an enabled-feature list contains a feature name.
- Game development: Check whether an inventory contains a particular item ID.
- Data cleanup: Skip records whose status appears in a list of blocked statuses.
For a few values or occasional checks, Vec::contains is simple and appropriate. For many repeated lookups in a large collection, a HashSet is often a better data structure.
Real Codebase Usage
In production Rust code, contains is often used inside validation and guard clauses.
A guard clause exits early when required data is missing:
fn can_publish(roles: &[String]) -> bool {
if !roles.contains(&String::from("editor")) {
return false;
}
true
}
When working with Vec<String>, iterators can avoid creating a temporary String for comparisons with a string literal:
fn can_publish(roles: &[String]) -> bool {
roles.iter().any(|role| role == "editor")
}
Developers also use any for normalized or partial checks:
let blocked_domains = vec!["spam.example", "invalid.example"];
let email = "person@spam.example";
let is_blocked = blocked_domains
.iter()
.any(|domain| email.ends_with(domain));
Choose contains when you need exact membership. Choose any when matching requires a rule, transformation, or multiple conditions.
Common Mistakes
Using Python-style in syntax
Rust does not support value in vector.
// This is not valid Rust.
if "-i" in n {
println!("yes");
}
Use contains instead:
if n.contains(&"-i") {
println!("yes");
}
Forgetting the reference
contains expects a reference to the target value.
let values = vec![1, 2, 3];
// Does not compile.
// values.contains(2);
Correct code:
assert!(values.contains(&2));
Comparing String values awkwardly
This works, but creates a new String only to search:
let names = vec![String::from("Ada")];
let exists = names.contains(&String::from("Ada"));
For a string literal, an iterator comparison is often clearer:
let exists = names.iter().any(|name| name == "Ada");
Expecting case-insensitive matching
contains performs an exact equality comparison. "Admin" and "admin" are different strings.
let roles = vec!["Admin"];
assert!(!roles.contains(&"admin"));
Use a custom iterator condition when case should not matter:
let has_admin = roles.iter().any(|role| role.eq_ignore_ascii_case("admin"));
Using a vector for many frequent lookups
contains searches from the start and can inspect every element. If you repeatedly check membership in a large, unique collection, consider HashSet instead.
Comparisons
| Need | Best tool | Why |
|---|---|---|
Check for one exact value in a Vec | values.contains(&target) | Short and readable |
| Check a custom condition | `values.iter().any( | value |
| Find the matching value itself | `values.iter().find( | value |
| Keep only matching values | `values.iter().filter( | value |
| Perform many membership checks | HashSet::contains(&target) | Usually faster lookup than scanning a vector |
contains and any can both answer “does a match exist?”:
let values = vec![1, 2, 3];
assert!(values.contains(&2));
assert!(values.iter().any(|value| *value == 2));
Prefer contains for exact equality. It communicates your intent directly.
Cheat Sheet
// Exact membership in Vec<T>
let values = vec![1, 2, 3];
let exists = values.contains(&2); // true
// String slices: Vec<&str>
let flags = vec!["-i", "--help"];
let exists = flags.contains(&"-i"); // true
// Custom predicate
let values = vec![1, 2, 3];
let has_even = values.iter().any(|value| value % 2 == 0); // true
// Get the first matching element
let found = values.iter().find(|value| **value == 2); // Some(&2)
containsreturnsbool.- Pass the target by reference:
contains(&target). containsperforms exact equality matching.containsstops once it finds a match.- Use
iter().any(...)for custom matching logic. - For many lookups in a large collection, evaluate whether
HashSet<T>fits better.
FAQ
How do I check whether a Rust vector contains a value?
Call contains and pass a reference to the target:
let values = vec![1, 2, 3];
assert!(values.contains(&2));
Why does contains need & in Rust?
contains borrows the target value for comparison. The & creates a reference, allowing Rust to compare the value without moving ownership.
Does Vec::contains return the matching item?
No. It returns only true or false. Use iter().find(...) if you need the matching element:
let found = vec![1, 2, 3].iter().find(|value| **value == 2);
Can I use contains with Vec<String> and a &str?
Vec::contains expects a reference to the vector's element type, so with Vec<String> it naturally accepts &String. For convenient comparisons to a &str, use:
names.iter().any(|name| name == "Ada")
Is contains case-sensitive for strings?
Yes. It uses exact equality. "YES" and "yes" do not match.
Does contains change the vector?
No. It only borrows the vector and searches it. The vector remains available afterward.
When should I use a HashSet instead of a Vec?
Use a HashSet when membership checks are frequent and the collection is large or represents unique values. Keep a Vec when ordering matters, duplicates are meaningful, or the collection is small.
Mini Project
Description
Build a small command-line flag checker. The program receives a fixed list of arguments and reports whether common flags were supplied. This mirrors a common task in command-line tools before they process files or make network requests.
Goal
Use Vec::contains to detect exact command-line flags and print the selected program settings.
Requirements
- Create a vector containing at least three command-line-style flags.
- Check whether the vector contains
"-i". - Check whether the vector contains
"--verbose". - Print a clear message for each enabled or disabled setting.
- Use
containsrather than writing a manual loop.
Keep learning
Related questions
Accepting Iterators in Rust Functions with impl Iterator
Learn how to write Rust functions that accept HashMap iterators using generic bounds or impl Iterator, avoiding trait object and lifetime errors.
Accessing Cargo Package Metadata in Rust
Learn how to read Cargo package metadata like version, name, and authors in Rust using compile-time environment macros.
Associated Types vs Generic Type Parameters in Rust: When to Use Each
Learn when to use associated types vs generic parameters in Rust traits, with clear rules, examples, and practical API design advice.