2016-04-29 3 views
0

Я не могу понять, почему моя локальная строка var does not live long enough. Вы можете увидеть мой код. Он работает на игровой площадке Rust.ошибка: `line` недолго проживает, но это нормально на детской площадке

Возможно, у меня есть идея проблемы: я использую структуру (нагрузка - это функция этой структуры). Поскольку я хочу сохранить результат строки в члене моей структуры, это может быть проблемой. Но я не вижу, что делать, чтобы решить эту проблему.

pub struct Config<'a> { 
    file: &'a str, 
    params: HashMap<&'a str, &'a str> 
} 

impl<'a> Config<'a> { 
    pub fn new(file: &str) -> Config { 
     Config { file: file, params: HashMap::new() } 
    } 

    pub fn load(&mut self) ->() { 
     let f = match fs::File::open(self.file) { 
      Ok(e) => e, 
      Err(e) => { 
       println!("Failed to load {}, {}", self.file, e); 
       return; 
      } 
     }; 
     let mut reader = io::BufReader::new(f); 
     let mut buffer = String::new(); 

     loop { 
      let result = reader.read_line(&mut buffer); 

      if result.is_ok() && result.ok().unwrap() > 0 { 
       let line: Vec<String> = buffer.split("=").map(String::from).collect(); 

       let key = line[0].trim(); 
       let value = line[1].trim(); 

       self.params.insert(key, value); 
      } 

      buffer.clear(); 
     } 
    } 
    ... 
} 

И я получаю эту ошибку:

src/conf.rs:33:27: 33:31 error: `line` does not live long enough 
src/conf.rs:33     let key = line[0].trim(); 
             ^~~~ 
src/conf.rs:16:34: 41:6 note: reference must be valid for the lifetime 'a as defined on the block at 16:33... 
src/conf.rs:16  pub fn load(&mut self) ->() { 
src/conf.rs:17   let f = match fs::File::open(self.file) { 
src/conf.rs:18    Ok(e) => e, 
src/conf.rs:19    Err(e) => { 
src/conf.rs:20     println!("Failed to load {}, {}", self.file, e); 
src/conf.rs:21     return; 
       ... 
src/conf.rs:31:87: 37:14 note: ...but borrowed value is only valid for the block suffix following statement 0 at 31:86 
src/conf.rs:31     let line: Vec<String> = buffer.split("=").map(String::from).collect(); 
src/conf.rs:32 
src/conf.rs:33     let key = line[0].trim(); 
src/conf.rs:34     let value = line[1].trim(); 
src/conf.rs:35 
src/conf.rs:36     self.params.insert(key, value); 
       ... 
+0

Добавьте определение структуры, чтобы мы могли узнать тип 'self.params'. – malbarbo

+0

все сделано, я добавил структуру – Kadok

ответ

1

Есть три стадии реализации, почему это не работает.

let line: Vec<String> = buffer.split("=").map(String::from).collect(); 
let key = line[0].trim(); 
let value = line[1].trim(); 
self.params.insert(key, value); 
  1. line является Vec из String с, что означает вектор принадлежат строки его содержащие. Эффект от этого заключается в том, что когда вектор освобождается от памяти, элементы, строки также освобождаются.
  2. Если мы посмотрим на string::trimhere, мы видим, что он принимает и возвращает &str. Другими словами, функция ничего не выделяет или не передает права собственности - возвращаемая строка просто представляет собой фрагмент исходной строки. Поэтому, если бы мы освободили исходную строку, у обрезанной строки не было бы достоверных данных.

  3. Подпись HashMap::insert является fn insert(&mut self, k: K, v: V) -> Option<V>. Функция перемещает как ключ, так и значение, потому что они должны быть действительными до тех пор, пока они могут находиться в хэш-карте. Мы хотели бы дать хэш-карту две строки. Однако и key, и value - это просто ссылки на строки, которые принадлежат вектору - мы просто заимствуем их, поэтому мы не можем их отдать.

Решение прост: скопируйте строки после их разделения.

let line: Vec<String> = buffer.split("=").map(String::from).collect(); 
let key = line[0].trim().to_string(); 
let value = line[1].trim().to_string(); 
self.params.insert(key, value); 

Это выделит две новые строки и скопирует обрезанные фрагменты в новые строки.


Мы могли бы переместил строку из вектора, если бы мы не обрезать строки после (т.е. с Vec::remove.); Мне не удалось найти простой способ обрезания строки без выделения нового.

Кроме того, как упоминает malbarbo, мы можем избежать дополнительного распределения, которое делается с помощью map(String::from), и создания вектора с collect(), просто опуская их.

+0

Благодарим вас за разъяснения! – Kadok

0

В этом случае вы должны использовать String вместо &str. См. this, чтобы понять разницу.

Вы также можете исключить создание промежуточного вектора и использовать возвращение итератора по split прямому

pub struct Config<'a> { 
    file: &'a str, 
    params: HashMap<String, String> 
} 

... 

let mut line = buffer.split("="); 
let key = line.next().unwrap().trim().to_string(); 
let value = line.next().unwrap().trim().to_string(); 
+0

большое спасибо!это намного проще! – Kadok

Смежные вопросы