在编写HTML解析器时,借用的值不够长

时间:2016-02-26 15:46:56

标签: html regex pointers rust

我是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标记正则表达式,请建议我可以使用它。

1 个答案:

答案 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 letif something.is_none()而非可变局部变量等。)。这或多或少是您原始代码的直接翻译。

至于使用正则表达式解析HTML,我无法拒绝提供链接to this answer。认真考虑使用正确的HTML解析器而不是依赖于正则表达式。