我是Rust的新手,并尝试构建HTML解析器。
我首先尝试解析字符串并将其放在Hashmap<&str, i32>
中。
我发现我必须处理信件案件。
所以我添加了tag.to_lowercase()
,这会创建String
类型。从那里它让我的大脑恐慌。
以下是我的代码段。
fn html_parser<'a>(html:&'a str, mut tags:HashMap<&'a str, i32>) -> HashMap<&'a str, i32>{
let re = Regex::new("<[:alpha:]+?[\\d]*[:space:]*>+").unwrap();
let mut count;
for caps in re.captures_iter(html) {
if !caps.at(0).is_none(){
let tag = &*(caps.at(0).unwrap().trim_matches('<').trim_matches('>').to_lowercase());
count = 1;
if tags.contains_key(tag){
count = *tags.get_mut(tag).unwrap() + 1;
}
tags.insert(tag,count);
}
}
tags
}
会抛出此错误,
src\main.rs:58:27: 58:97 error: borrowed value does not live long enough
src\main.rs:58 let tag:&'a str = &*(caps.at(0).unwrap().trim_matches('<').trim_matches('>').to_lowercase());
^~~~~~~~~~~~~~~~~~~
src\main.rs:49:90: 80:2 note: reference must be valid for the lifetime 'a as defined on the block at 49:89...
src\main.rs:49 fn html_parser<'a>(html:&'a str, mut tags:HashMap<&'a str, i32>)-> HashMap<&'a str, i32>{
src\main.rs:58:99: 68:6 note: ...but borrowed value is only valid for the block suffix following statement 0 at 58:98
src\main.rs:58 let tag:&'a str = &*(caps.at(0).unwrap().trim_matches('<').trim_matches('>').to_lowercase());
src\main.rs:63
...
error: aborting due to previous error
我读过Rust中的生命周期,但仍然无法理解这种情况。
如果有人有一个好的HTML标记正则表达式,请建议我可以使用它。
答案 0 :(得分:2)
要了解您的问题,查看函数签名非常有用:
lldb <executable>
(lldb) settings set target.input-path <file>
(lldb) process launch
从这个签名中我们可以粗略地看到,接受和返回的哈希映射只能由fn html_parser<'a>(html: &'a str, mut tags: HashMap<&'a str, i32>) -> HashMap<&'a str, i32>
的子句进行键控。但是,在您的代码中,您试图将一个完全不相关的字符串切片(在生命周期中)插入html
:
html
此处的第一个问题(您的特定错误就是这个问题)是您尝试从let tag = &*(caps.at(0).unwrap().trim_matches('<').trim_matches('>').to_lowercase());
返回的临时String
中取出一个切片。此临时字符串仅在此语句期间处于活动状态,因此当语句结束时,将取消分配字符串,如果编译器未禁止,则其引用将变为悬空。因此,编写此分配的正确方法如下:
to_lowercase()
(或者你可以使用top let tag = caps.at(0).unwrap().trim_matches('<').trim_matches('>').to_lowercase();
let tag = &*tag;
并在使用时将其转换为切片)
但是,即使在此更改后,您的代码也无法正常工作。 tag
方法在生命周期内分配与to_lowercase()
无关的新String
。因此,您从中获取的任何切片的生命周期必须比html
更短。因此,不可能将这样的切片作为键插入到映射中,因为它们指向的数据在此函数返回后可能无效(在此特定情况下,将无效)。
很难说出解决这个问题的最佳方法是什么,因为它可能取决于程序的整体架构,但最简单的方法是在函数内部创建一个新的'a
: p>
HashMap<String, i32>
我还将其代码更改为更惯用(fn html_parser(html:&str, tags: HashMap<&str, i32>) -> HashMap<String, i32>{
let mut result: HashMap<String, i32> = tags.iter().map(|(k, v)| (k.to_owned(), *v)).collect();
let re = Regex::new("<[:alpha:]+?[\\d]*[:space:]*>+").unwrap();
for caps in re.captures_iter(html) {
if let Some(cap) = caps.at(0) {
let tag = cap
.trim_matches('<')
.trim_matches('>')
.to_lowercase();
let count = result.get(&tag).unwrap_or(0) + 1;
result.insert(tag, count);
}
}
result
}
而非if let
,if something.is_none()
而非可变局部变量等。)。这或多或少是您原始代码的直接翻译。
至于使用正则表达式解析HTML,我无法拒绝提供链接to this answer。认真考虑使用正确的HTML解析器而不是依赖于正则表达式。