2016-06-23 5 views
4

Я пытаюсь написать функцию Rust, которая принимает регулярное выражение, и строку/str и возвращает HashMap всех названных захватов в этом регулярном выражении. Вот код:Regex захватов не живут так долго, как я думаю, они должны

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 также должен быть 't, который в этом случае равен 'a.

Мое определение функции имеет HashMap, который использует это время жизни 'a, так что не все ли это Just Work (tm)? Думаю, я понимаю, почему вы не можете использовать локальную переменную, если вы ее не вернете, но в этом случае я использую ссылки, которые должны жить достаточно долго, не так ли?

Я полагаю, что мог бы .clone() всем String, но мне любопытно, могу ли я написать это с помощью только ссылок. Разве это не должно быть более эффективным? Я немного новичок в Rust, поэтому я пытаюсь понять и сделать что-то правильным, продвинутым способом.

ответ

2

Ваше рассуждение верно, но вы забыли деталь:

regex::Regex::captures возвращаемое значение имеет срок службы 't, которая является такой же срок службы, как строки, в данном случае, это означает, что 'a, то regex::Captures::iter_named * возвращаемое значение также имеет такое же время жизни 't, что в данном случае равно 'a, а это означает, что (name, value) для этой вещи также должно быть 't, которое в этом случае равно 'a.

* 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.

+0

> 'regex :: Captures :: iter_named' также требует' '' '' '' '' '' '' ', то есть' & caps' должен иметь время жизни '' t' ('' a'). Но [функция 'captures'] (https://doc.rust-lang.org/regex/regex/struct.Regex.html#method.captures) должна возвращать что-то со временем жизни' 't', правильно? – Rory

+0

@Rory Я обновил ответ. – malbarbo

+0

Возможно, у вас не было полного ответа на мой вопрос о сроках службы, но вы решили основную проблему. :) – Rory