返回函数中serde_json创建的结构

时间:2017-03-07 09:18:03

标签: rust borrow-checker

我坚持看似简单的问题。我得到为什么我看到错误但似乎无法解决它。显然我缺少一些基本的东西。

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

这对我来说很有意义,因为我在函数中创建了struct并返回了一个借来的引用,当函数完成时当然会消失。

但是,当我更改返回类型为std::result::Result<MyAppDeployInfo, Error>(即不返回引用)时,我似乎无法使Ok(x)生效。 ..我收到一个错误:

expected struct `MyAppDeployInfo`, found reference

同样,这是有意义的,因为serde_json创建了一个结构,然后我遍历引用,所以当我索引到集合时,我正在查看引用。

所以我尝试了各种各样的东西来获取结构值,例如解除引用,Box::newclone()to_owned()等,但仍然无法使其工作。

我已经在这里搜索了所有问题,阅读了这本书等等,我仍然不清楚如何解决这个问题......任何指针都会受到赞赏。

2 个答案:

答案 0 :(得分:3)

在不了解您的项目的情况下(请在下次生成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"),
}

答案 1 :(得分: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,则会收到错误&#34;无法移出借来的内容&#34;,因为MyAppDeployInfo不是Copy,而您正试图移动。如果你写x.clone(),它应该有效,除非你换了别的东西吗?

或者,您可以从一开始就使用移动内容。如果您编写data.deploy_info.value.into_iter().filter(|x| x.app == "myapp"),则移出初始结构而不是复制它。然后,生成的VecMyAppDeployInfo作为其项目类型。然后你可以使用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"),
    }
}