Question
How can I check whether a Rust string contains a substring? In Python, I would use the in operator:
a = "abcd"
if "bc" in a:
do_something()
I tried the following Rust code:
let a = "abcd";
if "bc" in a {
do_something();
}
However, Rust reports expected '{', found 'in'. What is the idiomatic Rust syntax for testing whether one string contains another, and why is the compiler suggesting a block?
Short Answer
Rust does not use Python's in operator for substring checks. Use the contains method from the str type instead:
if a.contains("bc") {
do_something();
}
By the end, you will know how contains works with string slices and String values, how to write clear conditional checks, and when to use related search methods.
Concept
Rust checks for a substring with str::contains:
let a = "abcd";
if a.contains("bc") {
println!("Found it!");
}
contains returns a bool:
trueif the text occurs anywhere in the stringfalseif it does not occur
The method is available on str, Rust's string-slice type. A string literal such as "abcd" has type &str, so it can call contains directly. A String can also call it because Rust automatically borrows it as a string slice when needed.
Rust deliberately has no general Python-style in operator for this operation. Instead, methods make the operation explicit: contains describes exactly what the code is checking.
The compiler's block suggestion is not the solution. After parsing if "bc", Rust expects the body block beginning with {. It does not recognize in as part of an if condition, so its generic parser recovery advice is misleading for this intent.
Mental Model
Think of a string as a line of text and a substring as a short phrase you are trying to spot with a highlighter.
Text: a b c d
Search for: b c
a.contains("bc") asks: “Can I find this exact sequence of characters somewhere in this text?” The answer is a simple yes (true) or no (false), which makes it suitable for an if statement.
The method does not need to tell you where it found the text. It only answers whether the text exists. If you need the position, use find instead.
Syntax and Examples
The basic form is:
text.contains(pattern)
For a normal substring search, pass a string slice such as "rust".
fn main() {
let language = "Rust programming";
if language.contains("Rust") {
println!("The language name was found.");
}
if !language.contains("Python") {
println!("Python was not found.");
}
}
contains("Rust") evaluates to true, so the first message is printed. The ! operator means “not,” so the second condition is also true.
It works with an owned String too:
fn main() {
let filename = String::from("report-2025.csv");
if filename.contains(".csv") {
println!("This looks like a CSV file.");
}
}
You usually do not need to convert a String manually with as_str() just to call contains. Rust performs the required borrowing automatically.
Step by Step Execution
Consider this program:
fn main() {
let a = "abcd";
let search_text = "bc";
if a.contains(search_text) {
println!("Match found");
}
}
Execution proceeds as follows:
arefers to the string slice"abcd".search_textrefers to the string slice"bc".a.contains(search_text)searchesafor the exact consecutive charactersbfollowed byc.- The sequence occurs in
"abcd", socontainsreturnstrue. - Because the
ifcondition istrue, Rust runs the block and printsMatch found.
If search_text were "bd", contains would return false. The characters exist in a, but they are not adjacent, so "bd" is not a substring of "abcd".
Real World Use Cases
Substring checks appear in many everyday Rust programs:
- File handling: Check whether a filename contains a marker such as
"draft"before publishing it. - Log processing: Find lines containing
"ERROR"or"timeout". - HTTP handlers: Check whether a request header contains a required directive.
- Command-line tools: Search text files for a user-provided query.
- Validation: Reject a username containing a prohibited word.
- Data imports: Detect whether a CSV header contains a required column name.
Example: filtering log lines.
let line = "2025-03-08 ERROR database connection failed";
if line.contains("ERROR") {
eprintln!("An error log entry needs attention: {line}");
}
Real Codebase Usage
In production code, contains is often used as a readable condition, especially for quick filtering and validation.
Guard clauses
Return early when input contains prohibited content:
fn validate_comment(comment: &str) -> Result<(), String> {
if comment.contains("<script") {
return Err("HTML script tags are not allowed".to_owned());
}
Ok(())
}
Filtering a collection
Use filter when selecting items that contain text:
let files = ["notes.txt", "todo.txt", "image.png"];
let text_files: Vec<&str> = files
.iter()
.copied()
.filter(|name| name.contains(".txt"))
.collect();
assert_eq!(text_files, vec!["notes.txt", "todo.txt"]);
Configuration and feature checks
fn is_debug_mode(flags: &str) -> bool {
flags.contains("debug")
}
For structured values, prefer parsing over substring matching. For example, use std::path::Path::extension() to check a file extension rather than checking whether a filename merely contains ".csv".
Common Mistakes
Using Python syntax
This is invalid Rust:
// Does not compile
if "bc" in a {
println!("Found");
}
Use a method call instead:
if a.contains("bc") {
println!("Found");
}
Expecting case-insensitive matching
contains is case-sensitive:
assert!("Rust".contains("Rust"));
assert!(!"Rust".contains("rust"));
For simple ASCII-oriented case-insensitive checks, normalize both values:
let text = "Learn Rust";
let query = "RUST";
if text.to_lowercase().contains(&query.to_lowercase()) {
println!("Match");
}
This creates new String values. Unicode case-insensitive matching has additional language-specific considerations, so avoid assuming lowercase conversion is always equivalent to human-language case folding.
Confusing a substring with separate characters
assert!(!"abcd".contains("bd"));
Although b and d both appear, "bd" does not appear as one continuous sequence.
Assuming contains returns an index
contains returns bool, not a position. Use find if you need the byte index:
assert_eq!("abcd".find("bc"), Some(1));
Using contains for an exact match
// This is true for "administrator" too.
let is_admin = username.contains("admin");
Use equality when the complete value must match:
let is_admin = username == "admin";
Treating contains(".csv") as reliable extension validation
A name like report.csv.backup contains ".csv" but is not necessarily a CSV file. Use path-aware APIs when working with filesystem paths.
Comparisons
| Need | Recommended Rust operation | Example | Result |
|---|---|---|---|
| Test whether text occurs anywhere | contains | text.contains("cat") | bool |
| Find the first occurrence | find | text.find("cat") | Option<usize> |
| Test the beginning | starts_with | text.starts_with("https://") | bool |
| Test the end | ends_with | text.ends_with(".rs") | bool |
| Split around a delimiter | split | text.split(",") | iterator |
| Check complete equality | == | text == "cat" | bool |
contains is best when location does not matter. Choose find when you need to slice, report, or otherwise use the match location.
Also note that string indices returned by find are byte indices, because UTF-8 characters can use more than one byte. Do not assume every numeric byte position is a valid character boundary for arbitrary strings.
Cheat Sheet
// Basic substring test
let found = text.contains("needle");
// Use in a condition
if text.contains("needle") {
println!("Found");
}
// Negative test
if !text.contains("needle") {
println!("Not found");
}
// String literals (&str)
let text = "hello world";
assert!(text.contains("world"));
// Owned strings (String)
let text = String::from("hello world");
assert!(text.contains("world"));
// Related methods
assert!("hello".starts_with("he"));
assert!("hello".ends_with("lo"));
assert_eq!("hello".find("ll"), Some(2));
Key rules:
- Use
text.contains(pattern), notpattern in text. - Matching is case-sensitive.
- A match is consecutive text, not merely characters that appear somewhere.
containsreturnsbool.- An empty substring is contained in every string:
"abc".contains("")istrue.
FAQ
How do I check if a string contains another string in Rust?
Call contains:
if text.contains("search term") {
// match
}
Does contains work with String and &str?
Yes. It works on string slices (&str) and can be called directly on String values through automatic borrowing.
Is Rust contains case-sensitive?
Yes. "Rust".contains("rust") is false. Normalize or use a purpose-built comparison strategy if case should not matter.
Why does Rust reject the in operator?
Rust does not define Python-style in syntax for substring membership. After an if condition, its parser expects a { block, which is why the resulting syntax error mentions {.
How do I get the position of a substring in Rust?
Use find:
let position = "abcd".find("bc");
assert_eq!(position, Some(1));
Does contains use regular expressions?
No. contains("a.b") searches for the literal characters a, ., and b. Use a regex library when you specifically need pattern matching rules.
What happens when I search for an empty string?
It returns true, because an empty sequence exists at every boundary in a string:
assert!("abc".contains(""));
Mini Project
Description
Build a small log scanner that examines application log entries and reports entries containing a search term. This mirrors a common command-line and monitoring task: quickly locating warnings or errors in text output.
Goal
Print every log entry that contains a user-selected search term, along with the number of matching entries.
Requirements
Use a collection containing at least five log messages.
Store the search term in a variable.
Check each log message with contains.
Print matching log messages.
Print the total number of matches.
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.