【发布时间】:2021-09-04 16:14:40
【问题描述】:
我有一个serde_json::Value,我希望它包含一个对象数组。从这些对象中,我想提取 2 个值并在任何失败时返回错误。到目前为止,这是我的代码:
use std::collections::HashMap;
use anyhow::Result;
fn get_stock(response: serde_json::Value) -> Result<HashMap<String, u32>>{
response["products"]
.as_array()?.iter()
.map(|product| {
let product = product.as_object()?;
(
product["name"].as_str()?.to_owned(),
//as_u64 fails for some reason
product["stock"].as_str()?.parse::<u32>()?,
)
})
.collect()?
}
当我使用 .unwrap() 时,它运行良好,但在将返回类型更改为 Result 并用 ? 替换 unwraps 后,我收到以下编译错误:
error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result`
--> src/bin/products.rs:7:20
|
5 | / fn get_stock(response: serde_json::Value) -> Result<HashMap<String, u32>>{
6 | | response["products"]
7 | | .as_array()?.iter()
| | ^ use `.ok_or(...)?` to provide an error compatible with `Result<HashMap<std::string::String, u32>, anyhow::Error>`
8 | | .map(|product| {
... |
16 | | .collect()?
17 | | }
| |_- this function returns a `Result`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<HashMap<std::string::String, u32>, anyhow::Error>`
= note: required by `from_residual`
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> src/bin/products.rs:9:46
|
8 | .map(|product| {
| ______________-
9 | | let product = product.as_object()?;
| | ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
10 | | (
11 | | product["name"].as_str()?.to_owned(),
... |
14 | | )
15 | | })
| |_________- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `(std::string::String, u32)`
= note: required by `from_residual`
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> src/bin/products.rs:11:41
|
8 | .map(|product| {
| ______________-
9 | | let product = product.as_object()?;
10 | | (
11 | | product["name"].as_str()?.to_owned(),
| | ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
... |
14 | | )
15 | | })
| |_________- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `(std::string::String, u32)`
= note: required by `from_residual`
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> src/bin/products.rs:13:42
|
8 | .map(|product| {
| ______________-
9 | | let product = product.as_object()?;
10 | | (
11 | | product["name"].as_str()?.to_owned(),
12 | | //as_u64 fails for some reason
13 | | product["stock"].as_str()?.parse::<u32>()?,
| | ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
14 | | )
15 | | })
| |_________- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `(std::string::String, u32)`
= note: required by `from_residual`
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> src/bin/products.rs:13:58
|
8 | .map(|product| {
| ______________-
9 | | let product = product.as_object()?;
10 | | (
11 | | product["name"].as_str()?.to_owned(),
12 | | //as_u64 fails for some reason
13 | | product["stock"].as_str()?.parse::<u32>()?,
| | ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
miav@battlestation catbot % cargo run --bin products
Compiling catbot v0.1.0 (/Users/miav/Documents/Personal/Projects/programming/catbot)
error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result`
--> src/bin/products.rs:7:20
|
5 | / fn get_stock(response: serde_json::Value) -> Result<HashMap<String, u32>>{
6 | | response["products"]
7 | | .as_array()?.iter()
| | ^ use `.ok_or(...)?` to provide an error compatible with `Result<HashMap<std::string::String, u32>, anyhow::Error>`
8 | | .map(|product| {
... |
16 | | .collect()?
17 | | }
| |_- this function returns a `Result`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<HashMap<std::string::String, u32>, anyhow::Error>`
= note: required by `from_residual`
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> src/bin/products.rs:9:46
|
8 | .map(|product| {
| ______________-
9 | | let product = product.as_object()?;
| | ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
10 | | (
11 | | product["name"].as_str()?.to_owned(),
... |
14 | | )
15 | | })
| |_________- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `(std::string::String, u32)`
= note: required by `from_residual`
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> src/bin/products.rs:11:41
|
8 | .map(|product| {
| ______________-
9 | | let product = product.as_object()?;
10 | | (
11 | | product["name"].as_str()?.to_owned(),
| | ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
... |
14 | | )
15 | | })
| |_________- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `(std::string::String, u32)`
= note: required by `from_residual`
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> src/bin/products.rs:13:42
|
8 | .map(|product| {
| ______________-
9 | | let product = product.as_object()?;
10 | | (
11 | | product["name"].as_str()?.to_owned(),
12 | | //as_u64 fails for some reason
13 | | product["stock"].as_str()?.parse::<u32>()?,
| | ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
14 | | )
15 | | })
| |_________- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `(std::string::String, u32)`
= note: required by `from_residual`
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> src/bin/products.rs:13:58
|
8 | .map(|product| {
| ______________-
9 | | let product = product.as_object()?;
10 | | (
11 | | product["name"].as_str()?.to_owned(),
12 | | //as_u64 fails for some reason
13 | | product["stock"].as_str()?.parse::<u32>()?,
| | ^ cannot use the `?` operator in a closure that returns `(std::string::String, u32)`
14 | | )
15 | | })
| |_________- this function should return `Result` or `Option` to accept `?`
|
= help: the trait `FromResidual<Result<Infallible, ParseIntError>>` is not implemented for `(std::string::String, u32)`
= note: required by `from_residual`
error: aborting due to 5 previous errors
我事先不知道 JSON 的确切结构,因此无法将其解析为结构。我所知道的是,它很可能是一个对象数组,其中包含一个 name 字符串字段和一个 stock 整数字段,我想将其提取到地图中。如果情况并非如此,那么我想返回一个错误。最简单的方法是什么?
更新: 在遵循 Lagerbaer 的建议并进行了一些挖掘之后,我想出了以下解决方案。
use anyhow::{anyhow, Result};
use std::collections::HashMap;
fn get_stock(response: serde_json::Value) -> Result<HashMap<String, u32>> {
response
.get("products")
.ok_or(anyhow!("'products' not found"))?
.as_array()
.ok_or(anyhow!("'products' is not an array"))?
.iter()
.map(|product| -> Result<(String, u32)> {
let product = product.as_object().unwrap();
Ok((
product
.get("name")
.ok_or(anyhow!("'name' not found"))?
.as_str()
.ok_or(anyhow!("'name' is not a string"))?
.trim()
.to_owned(),
//as_u64 fails for some reason
product
.get("stock")
.ok_or(anyhow!("'stock' not found"))?
.as_str()
.ok_or(anyhow!("'stock' is not a string"))?
.parse::<u32>()?,
))
})
.collect()
}
使用ok_or() 将Option 映射到Result,必须使用anyhow! 宏使其与anyhow 结果类型兼容。
事实证明,collect() 实际上接受了一个迭代器
Result<(String, u32)> 生成具有我想要的确切行为的Result<HashMap<String, u32>>,即返回第一个错误或如果没有错误则返回完整的哈希映射。
【问题讨论】:
标签: json rust serde serde-json