Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning a struct created by serde_json in a function

I'm stuck on what seems like a simple issue. I get why I am seeing the error but can't seem to resolve it. Obviously I am missing something fundamental.

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),
    }
}

The error I get is:

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

Which makes sense to me, as I am creating the struct in the function and returning a borrowed reference, which of course goes away when the function is finished.

But, when I change the return type be std::result::Result<MyAppDeployInfo, Error> (that is, not returning a reference) I can't seem to get Ok(x) to work...I get an error:

expected struct `MyAppDeployInfo`, found reference

Again, this makes sense as serde_json creates a structure and then I iterate through references, so when I index into the collection I am looking at a reference.

So I tried all sorts of things to get the struct value like dereferencing, Box::new, clone(), to_owned(), etc and still can't get it to work.

I've searched all the issues here, read the book, etc and it is still not clear to me how I can resolve this...any pointers would be appreciated.

like image 694
Chris Avatar asked Sep 12 '25 11:09

Chris


1 Answers

Without knowing more about your project (please produce an MCVE next time), I'd say that you can change the .iter() call into .into_iter(). Instead of collecting into a Vec and then using get, I'd simply work with the iterator directly:

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"),
}
like image 153
oli_obk Avatar answered Sep 14 '25 03:09

oli_obk