2017-02-26 34 views
1

不幸的是,在Rust中管理與字符串和結構有關的生命週期有很多麻煩。鐵鏽變量「活不夠長」和「衝突的要求」

#[macro_use] 
extern crate serde_derive; 
extern crate serde_json; 
use serde_json::Value; 
use std::fs::File; 
use std::path::Path; 
use std::io::prelude::*; 
use std::fs; 
use std::cell::RefCell; 

#[derive(Serialize, Deserialize, Debug)] 
struct Song { 
    artist: String, 
} 

struct SongEntry { 
    date: &'static str, 
    song: &'static Song, 
} 

fn main() { 
    let paths = fs::read_dir("./charts/").unwrap(); 

    let fileContents = paths.map(| path | { 
     let p = path.unwrap().path(); 
     let file = File::open(&p).unwrap(); 
     let v: Vec<Song> = serde_json::from_reader(file).unwrap(); 
     return v.iter().map(move | song | { 
      let date = p.file_stem().unwrap().to_str().unwrap(); 
      return SongEntry { 
       song: song, 
       date: date, 
      }; 
     }) 
    }); 
} 

我已經試過這裏管理內存的許多變化,但似乎只是換一個錯誤另一個。

目的是遍歷目錄中的JSON文件,解析它們,並彙編包含日期(來自文件名)和內容(來自解析的JSON)的對象向量。

到目前爲止,我已經試過內map中聲明date,或者在它之外,嘗試使用Arc管理date變量,試圖內部循環使用和不使用move關鍵字。

但是,我只是無法找到一種方法來獲取這些map方法中的變量綁定以保持適當的時間。任何幫助將非常感激。

產生的電流誤差:

Compiling kanye v0.1.0 (file:///Users/tmcw/src/sandbox/kanye) 
error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements 
    --> src/main.rs:29:26 
    | 
29 |    let date = p.file_stem().unwrap().to_str().unwrap(); 
    |       ^^^^^^^^^ 
    | 
note: first, the lifetime cannot outlive the lifetime as defined on the body at 28:42... 
    --> src/main.rs:28:43 
    | 
28 |   return v.iter().map(move | song | { 
    | ___________________________________________^ starting here... 
29 | |    let date = p.file_stem().unwrap().to_str().unwrap(); 
30 | |    return SongEntry { 
31 | |     song: song, 
32 | |     date: date, 
33 | |    }; 
34 | |   }) 
    | |_________^ ...ending here 
note: ...so that closure can access `p` 
    --> src/main.rs:29:24 
    | 
29 |    let date = p.file_stem().unwrap().to_str().unwrap(); 
    |      ^
    = note: but, the lifetime must be valid for the static lifetime... 
note: ...so that reference does not outlive borrowed content 
    --> src/main.rs:32:23 
    | 
32 |     date: date, 
    |      ^^^^ 

error: aborting due to previous error 

error: Could not compile `kanye`. 

To learn more, run the command again with --verbose. 

元的問題:這是鐵鏽的適當使用map,或者我應該用正常的迭代呢?我也嘗試過迭代,但也受到內存檢查的阻礙。

回答

2

如果要將引用存儲在一個結構中,那麼這些引用必須引用擁有該結構的所有者的對象。此外,&'static引用必須引用在整個程序執行期間有效的對象。 datesong字段都不是這樣。

這裏,SongEntry結構應該只擁有datesong對象。

struct SongEntry { 
    date: String, 
    song: Song, 
} 

對於date字段,只需將字符串切片(&str)轉換成擁有串(String)與to_string方法。對於song字段,您需要將所有權從矢量v中移出。但Vec::iter只能借用對其項目的借用參考。您必須使用Vec::into_iter,它將直接返回值,消耗流程中的向量。

fn main() { 
    let paths = fs::read_dir("./charts/").unwrap(); 

    let file_contents = paths.map(|path| { 
     let p = path.unwrap().path(); 
     let file = File::open(&p).unwrap(); 
     let v: Vec<Song> = serde_json::from_reader(file).unwrap(); 
     v.into_iter().map(move |song| { 
      let date = p.file_stem().unwrap().to_str().unwrap().to_string(); 
      SongEntry { 
       date: date, 
       song: song, 
      } 
     }) 
    }); 
} 

此時,file_contents超過SongEntry對象的迭代的迭代器(超過文件的外迭代迭代,用在一個文件中的條目內迭代迭代)。如果您希望直接使用SongEntry對象的迭代器,請使用flat_map而不是(paths)。如果需要,您可以在最終迭代器上使用collect將結果收集到矢量中(如果您只打算迭代一次結果,請不要使用collect!)。

+0

感謝您的優秀和徹底的答案!解釋如此之多。 – tmcw