2017-03-07 24 views
2

我被困在看起來像一個簡單問題的東西上。我得到爲什麼我看到錯誤,但似乎無法解決它。顯然我缺少一些基本的東西。在函數中返回由serde_json創建的結構體

fn terraform_deploy_info<'a>(app: &'a MyApp) -> std::result::Result<&MyAppDeployInfo, Error> { 
    let terraform = process::Command::new("terraform") 
      // We are querying output values. 
      .arg("output") 
      // We want it in json format for easy processing. 
      .arg("-json") 
      .output() 
      .expect("failed to execute terraform"); 

    let output = String::from_utf8_lossy(&terraform.stdout); 
    let data: TerraformOutputs = serde_json::from_str(&output).unwrap(); 

    let m = data.deploy_info.value.iter().filter(|&x| x.app == "myapp").collect::<Vec<_>>(); 

    if m.len() > 1 { 
     return Err(Error::MultipleDeployInfo); 
    } 

    match m.get(0) { 
     Some(&x) => Ok(x), 
     None => Err(Error::NoDeployInfo), 
    } 
} 

我得到的錯誤是:

borrowed value must be valid for the lifetime 'a as defined on the body at 

這對我來說很有意義,因爲我創造的功能結構和返回借用的引用,這當然會消失,當功能完了。

,當我改變的返回類型爲std::result::Result<MyAppDeployInfo, Error>(即,不返回的引用)我似乎無法得到Ok(x)工作...我得到一個錯誤:

expected struct `MyAppDeployInfo`, found reference 

再次,這是有道理的,因爲serde_json創建一個結構,然後我遍歷引用,所以當我索引到集合中時,我正在查看引用。

所以我嘗試了各種各樣的東西來得到類似於解引用,Box::newclone()to_owned()等結構值,仍然無法讓它工作。

我已經在這裏搜索了所有的問題,讀了這本書等,它仍然不清楚我如何解決這個問題......任何指針將不勝感激。

回答

2

不知道更多關於您的項目(請在下次生成MCVE),我想說您可以將.iter()呼叫更改爲.into_iter()。相反,收集到Vec,然後使用get的,我乾脆直接迭代工作:

let m = data.deploy_info.value.into_iter().filter(|&x| x.app == "myapp").fuse(); 

match (m.next(), m.next()) { 
    (None, None) => Err(Error::NoDeployInfo), 
    (Some(x), None) => Ok(x), 
    (Some(_), Some(_)) => Err(Error::MultipleDeployInfo), 
    (None, Some(_)) => panic!("Iterator::fuse broken"), 
} 
+0

真棒,謝謝! 'into_iter()'並不使用'collect()'是我所缺少的。現在起作用了。 – LegNeato

2

觀察類型的片斷。

let m = data.deploy_info.value // value is a Vec<MyAppDeployInfo> 
    .iter() // returns a Iterator<Item=&MyAppDeployInfo> 
    .filter(|&x| x.app == "myapp") 
    .collect::<Vec<_>>(); // collects into a Vec<&MyAppDeployInfo> 

if m.len() > 1 { 
    return Err(Error::MultipleDeployInfo); 
} 

match m.get(0) { // get() returns a reference to an element 
       // i.e. a &&MyAppDeployInfo 
     Some(&x) // pattern match says x : &MyAppDeployInfo 
      => Ok(x), // which matches the return type 
         // but you get a borrowing error. 
     None => Err(Error::NoDeployInfo), 
    } 
} 

現在,如果你改變返回類型Result<MyAppDeployInfo, Error>,你應該,你得到的不匹配類型的問題,因爲x是一個參考。如果您取消x,則會出現錯誤「無法移出借用內容」,因爲MyAppDeployInfo不是Copy,而您正在嘗試移動。如果你寫x.clone(),它應該工作,除非你改變了別的東西?

或者,您可以從一開始就處理移動內容。如果您寫入data.deploy_info.value.into_iter().filter(|x| x.app == "myapp"),則移出初始結構而不是複製它。那麼結果Vec將具有MyAppDeployInfo作爲其項目類型。然後,您可以製作mut並使用pop()而不是get(0)獲取可移出的唯一元素。

或者你可以做什麼@ker推薦,而不是首先使用collect()。我仍然切換到into_iter()雖然,使這個最終代碼:

fn terraform_deploy_info(app: &MyApp) // no explicit lifetime needed 
     -> std::result::Result<MyAppDeployInfo, Error> { 
    let data = // ... 

    let mut m = data.deploy_info.value.into_iter() 
     .filter(|x| x.app == "myapp").fuse(); 

    match (m.next(), m.next()) { 
     (None, None) => Err(Error::NoDeployInfo), 
     (Some(x), None) => Ok(x), 
     (Some(_), Some(_)) => Err(Error::MultipleDeployInfo), 
     (None, Some(_)) => panic!("Iterator::fuse broken"), 
    } 
} 
+0

非常好,謝謝你的詳細解釋!我現在完全理解它。 – LegNeato