在函数中返回由 serde_json 创建的结构
Returning a struct created by serde_json in a function
我被一个看似简单的问题困住了。我得到 为什么 我看到错误但似乎无法解决它。显然我遗漏了一些基本的东西。
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
这对我来说很有意义,因为我在函数中创建结构并return借用引用,当函数完成时它当然会消失。
但是,当我将 return 类型更改为 std::result::Result<MyAppDeployInfo, Error>
(也就是说,不是 return 引用)我可以'似乎 Ok(x)
无法正常工作...我收到错误消息:
expected struct `MyAppDeployInfo`, found reference
同样,这是有道理的,因为 serde_json
创建一个结构,然后我遍历引用,所以当我索引到集合中时,我正在查看一个引用。
所以我尝试了各种方法来获取结构值,例如取消引用、Box::new
、clone()
、to_owned()
等,但仍然无法让它工作。
我已经在这里搜索了所有问题,阅读了这本书等,但我仍然不清楚如何解决这个问题...任何指点将不胜感激。
在不了解更多关于您的项目的情况下(请下次制作一个 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"),
}
观察您的代码段的类型。
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),
}
}
现在,如果您将 return 类型更改为 Result<MyAppDeployInfo, Error>
,您会遇到类型不匹配的问题,因为 x
是引用。如果取消引用 x
,则会收到错误 "cannot move out of borrowed content",因为 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"),
}
}
我被一个看似简单的问题困住了。我得到 为什么 我看到错误但似乎无法解决它。显然我遗漏了一些基本的东西。
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
这对我来说很有意义,因为我在函数中创建结构并return借用引用,当函数完成时它当然会消失。
但是,当我将 return 类型更改为 std::result::Result<MyAppDeployInfo, Error>
(也就是说,不是 return 引用)我可以'似乎 Ok(x)
无法正常工作...我收到错误消息:
expected struct `MyAppDeployInfo`, found reference
同样,这是有道理的,因为 serde_json
创建一个结构,然后我遍历引用,所以当我索引到集合中时,我正在查看一个引用。
所以我尝试了各种方法来获取结构值,例如取消引用、Box::new
、clone()
、to_owned()
等,但仍然无法让它工作。
我已经在这里搜索了所有问题,阅读了这本书等,但我仍然不清楚如何解决这个问题...任何指点将不胜感激。
在不了解更多关于您的项目的情况下(请下次制作一个 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"),
}
观察您的代码段的类型。
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),
}
}
现在,如果您将 return 类型更改为 Result<MyAppDeployInfo, Error>
,您会遇到类型不匹配的问题,因为 x
是引用。如果取消引用 x
,则会收到错误 "cannot move out of borrowed content",因为 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"),
}
}