{"tool_call":"file.create","args":{"content":"# What are the differences between regex::Regex::captures and regex::Regex::captures_iter for handling multiple matches?
Regex::captures returns a single Captures object representing the first match in the input, providing access to the entire match and all capture groups for that match. Regex::captures_iter returns an iterator that yields a Captures object for each non-overlapping match in the input, allowing you to process all matches sequentially. The key distinction is that captures finds one match and stops, while captures_iter continues finding subsequent matches. Both methods handle capture groups identicallyâthe difference is purely about iteration over multiple match locations. Use captures when you need only the first match or expect exactly one match, and captures_iter when you need to process all matches in the input.
Basic captures Usage
rust\nuse regex::Regex;\n\nfn main() {\n let re = Regex::new(r\"(\\d+)-(\\w+)\").unwrap();\n let text = \"123-abc 456-def 789-xyz\";\n \n // captures returns Option<Captures>\n if let Some(caps) = re.captures(text) {\n println!(\"Full match: {}\", &caps[0]); // \"123-abc\"\n println!(\"First group: {}\", &caps[1]); // \"123\"\n println!(\"Second group: {}\", &caps[2]); // \"abc\"\n }\n \n // Only the first match is returned\n // \"456-def\" and \"789-xyz\" are not accessible\n}\n\n\ncaptures returns the first match with all its capture groups.
Basic captures_iter Usage\n\nrust\nuse regex::Regex;\n\nfn main() {\n let re = Regex::new(r\"(\\d+)-(\\w+)\").unwrap();\n let text = \"123-abc 456-def 789-xyz\";\n \n // captures_iter returns an iterator\n for caps in re.captures_iter(text) {\n println!(\"Match: {}, Groups: ({}, {})\", &caps[0], &caps[1], &caps[2]);\n }\n \n // Output:\n // Match: 123-abc, Groups: (123, abc)\n // Match: 456-def, Groups: (456, def)\n // Match: 789-xyz, Groups: (789, xyz)\n}\n\n\ncaptures_iter yields each match with its capture groups.
Return Type Differences
rust\nuse regex::Regex;\n\nfn main() {\n let re = Regex::new(r\"(\\w+)@(\\w+)\").unwrap();\n let text = \"a@b c@d e@f\";\n \n // captures: Option<Captures>\n let first: Option<regex::Captures> = re.captures(text);\n \n // captures_iter: CaptureMatches iterator\n let matches: regex::CaptureMatches = re.captures_iter(text);\n \n // CaptureMatches implements Iterator<Item = Captures>\n let all_matches: Vec<regex::Captures> = re.captures_iter(text).collect();\n \n // all_matches.len() == 3\n}\n\n\ncaptures returns Option<Captures>; captures_iter returns an iterator.
Single Match vs All Matches
rust\nuse regex::Regex;\n\nfn main() {\n let re = Regex::new(r\"(\\d+)\").unwrap();\n let text = \"Numbers: 10, 20, 30, 40\";\n \n // captures: first match only\n match re.captures(text) {\n Some(caps) => println!(\"First number: {}\", &caps[1]),\n None => println!(\"No numbers found\"),\n }\n // Output: First number: 10\n \n // captures_iter: all matches\n let numbers: Vec<i32> = re.captures_iter(text)\n .map(|caps| caps[1].parse().unwrap())\n .collect();\n \n println!(\"All numbers: {:?}\", numbers);\n // Output: All numbers: [10, 20, 30, 40]\n}\n\n\nUse captures when you need only the first; captures_iter for all matches.
Non-Overlapping Matches
rust\nuse regex::Regex;\n\nfn main() {\n // Both methods find non-overlapping matches\n let re = Regex::new(r\"ana\").unwrap();\n let text = \"banana\";\n \n // captures finds first \"ana\" (at position 1)\n if let Some(caps) = re.captures(text) {\n println!(\"First match: {}\", &caps[0]); // \"ana\" at \"banana[ana]\"\n }\n \n // captures_iter finds all non-overlapping matches\n for caps in re.captures_iter(text) {\n println!(\"Match: {}\", &caps[0]);\n }\n // Only one match found: \"ana\"\n // The \"ana\" at the end overlaps with the first, so it's skipped\n \n // This is the same behavior for both - they find non-overlapping matches\n}\n\n\nBoth methods find non-overlapping matches; captures_iter continues after each match.
Empty Matches
rust\nuse regex::Regex;\n\nfn main() {\n // Pattern that can match empty strings\n let re = Regex::new(r\"\").unwrap();\n let text = \"abc\";\n \n // captures: first empty match\n if let Some(caps) = re.captures(text) {\n println!(\"Match at position: {}\", caps.get(0).unwrap().start());\n }\n \n // captures_iter: empty matches at each position\n // But regex crate prevents infinite loops on empty matches\n // It advances at least one character after an empty match\n for (i, caps) in re.captures_iter(text).enumerate() {\n if let Some(m) = caps.get(0) {\n println!(\"Match {}: position {}\", i, m.start());\n }\n }\n // Matches at positions 0, 1, 2, 3 (end of string)\n}\n\n\nBoth handle empty matches; captures_iter advances position between matches.
Accessing Capture Groups
rust\nuse regex::Regex;\n\nfn main() {\n let re = Regex::new(r\"(?P<year>\\d{4})-(?P<month>\\d{2})-(?P<day>\\d{2})\").unwrap();\n let text = \"2023-01-15 and 2024-12-25\";\n \n // Single match: access groups by index or name\n if let Some(caps) = re.captures(text) {\n println!(\"Year: {}\", &caps[1]);\n println!(\"Month: {}\", &caps.name(\"month\").unwrap().as_str());\n println!(\"Day: {}\", &caps[\"day\"]);\n }\n \n // Iterator: same access pattern for each match\n for caps in re.captures_iter(text) {\n println!(\n \"Date: {}-{}-{}\",\n &caps[\"year\"],\n &caps[\"month\"],\n &caps[\"day\"]\n );\n }\n // Output:\n // Date: 2023-01-15\n // Date: 2024-12-25\n}\n\n\nBoth methods provide identical access to capture groups within each match.
Captures Object Lifetime
rust\nuse regex::Regex;\n\nfn main() {\n let re = Regex::new(r\"(\\w+)::(\\w+)\").unwrap();\n let text = String::from(\"foo::bar baz::qux\");\n \n // captures borrows the text\n let caps = re.captures(&text).unwrap();\n let match_str: &str = &caps[0]; // borrows from text\n \n // Valid: text is still in scope\n println!(\"Match: {}\", match_str);\n \n // captures_iter borrows text for duration of iteration\n let matches: Vec<&str> = re.captures_iter(&text)\n .map(|caps| caps[0].to_string()) // must own if storing\n .collect();\n \n // Can't return references from iterator that outlive iteration\n}\n\n\nBoth borrow the input text; extracted strings reference the original text.
Extracting All Matches
rust\nuse regex::Regex;\n\nfn main() {\n let re = Regex::new(r\"\\b\\w+\\b\").unwrap();\n let text = \"hello world rust programming\";\n \n // Using captures: must call repeatedly\n // This doesn't work - captures always starts from beginning\n let first = re.captures(text).map(|c| c[0].to_string());\n let first_again = re.captures(text).map(|c| c[0].to_string());\n // Same result both times!\n \n // Using captures_iter: natural iteration\n let words: Vec<String> = re.captures_iter(text)\n .map(|caps| caps[0].to_string())\n .collect();\n \n println!(\"Words: {:?}\", words);\n // Words: [\"hello\", \"world\", \"rust\", \"programming\"]\n}\n\n\ncaptures_iter is the correct way to get all matches; captures always returns the first.
