我试图编写一个带有正则表达式的Rust函数,以及一个字符串/ str并返回该正则表达式中所有已命名捕获的HashMap
。这是代码:
use std::collections::HashMap;
use regex::Regex;
fn get_matches<'a>(line: &'a str, re: &Regex) -> HashMap<&'a str, &'a str> {
let mut results = HashMap::new();
match re.captures(line) {
None => { return results; },
Some(caps) => {
for (name, value) in caps.iter_named() {
if let Some(value) = value {
results.insert(name, value);
}
}
}
}
results
}
我得到这个编译器错误(Rust 1.9.0):
error: `caps` does not live long enough
for (name, value) in caps.iter_named() {
^~~~
note: reference must be valid for the lifetime 'a as defined on the block at 6:79...
fn get_matches<'a>(line: &'a str, re: &Regex) -> HashMap<&'a str, &'a str> {
let mut results = HashMap::new();
match re.captures(line) {
None => { return results; },
Some(caps) => {
...
note: ...but borrowed value is only valid for the match at 9:8
match re.captures(line) {
None => { return results; },
Some(caps) => {
for (name, value) in caps.iter_named() {
if let Some(value) = value {
results.insert(name, value);
...
但是,我不明白。在这种情况下,regex::Regex::captures
return value has a lifetime of 't
, which is the same lifetime as the string表示'a'
,regex::Captures::iter_named
returned value also has the same lifetime of 't
,在这种情况下为'a
,这意味着(name, value)
for that thing也应该是{{} 1}},在本例中为't
。
我的函数定义有一个使用'a
生命周期的HashMap
,所以不应该只使用Just Work(tm)吗?我想我明白为什么你不能使用局部变量,除非你退回它,但在这种情况下,我使用的引用应该足够长,对吗?
我想我可以'a
.clone()
的所有内容,但我很好奇我是否可以仅使用引用来编写此内容。那不应该更高效吗?我对Rust有点新意,所以我试图以适当,先进的方式处理事情并做事。
答案 0 :(得分:2)
你的推理是正确的,但你忘记了一个细节:
regex::Regex::captures
返回值的生命周期为't
,这与字符串的生命周期相同,在本例中,这意味着'a
,regex::Captures::iter_named
*返回值也具有't
的相同生命周期,在这种情况下为'a
,这意味着该事件的(name, value)
也应为't
,在本例中为'a
{ {1}}。
* regex::Captures::iter_named
还需要&'t self
,即&caps
必须有生命周期't
(在这种情况下为'a
)。
请注意,编译器并未抱怨results
,而是抱怨caps
。 regex::Regex::captures
会返回caps: Captures<'a>
,这意味着上限会包含生命周期'a
。但要致电regex::Captures::iter_named
,必须使用生命周期'a
(iter_named
参数为&'a self = &'a Captures<'a>
)的引用。 虽然caps
包含生命周期'a
,但它没有生命周期'a
(生命周期只是Some
手臂。)
我不知道iter_named
如何使用空名称处理捕获,但这是一个只返回命名捕获的实现:
extern crate regex;
use std::collections::HashMap;
use regex::Regex;
fn get_matches<'a>(line: &'a str, re: &'a Regex) -> HashMap<&'a str, &'a str> {
let mut results = HashMap::new();
match re.captures(line) {
None => {
return results;
}
Some(caps) => {
for name in re.capture_names() {
if let Some(name) = name {
if let Some(value) = caps.name(name) {
results.insert(name, value);
}
}
}
}
}
results
}
这可能比iter_named
慢。