如何从Option中提取数据以供独立使用?

23

有没有一种方法可以从 Option 中“提取”数据?我有一个 API 调用返回 Some(HashMap)。我想使用 HashMap,就好像它不在 Some 里面,并操作这些数据。

根据我所读的,看起来 Some(...) 只适用于匹配比较和一些内置函数。

从板条箱文档中提取的简单 API 调用:

use std::collections::HashMap;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let resp = reqwest::blocking::get("https://httpbin.org/ip")?
        .json::<HashMap<String, String>>()?;
    println!("{:#?}", resp.get("origin"));
    Ok(())
}

结果:

Some("75.69.138.107")

一些示例在此完整记录:https://doc.rust-lang.org/rust-by-example/error/option_unwrap.html - Code4R7
4个回答

30
if let Some(origin) = resp.get("origin") {
    // use origin
}

如果你能保证这个值不可能是 None,那么你可以使用以下代码:
let origin = resp.get("origin").unwrap();

或者:

let origin = resp.get("origin").expect("This shouldn't be possible!");

另外,由于您的函数返回一个 Result

let origin = resp.get("origin").ok_or("This shouldn't be possible!")?;

或者使用自定义错误类型:

let origin = resp.get("origin").ok_or(MyError::DoesntExist)?;

10
最常见的方法是使用 if let:
if let Some(origin) = resp.get("origin") {
    origin.do_stuff()
}

如果您需要更细粒度的控制,可以使用模式匹配:

match resp.get("origin") {
    Some(origin) => origin.do_stuff(),
    None => panic!("origin not found!")
}

您也可以使用unwrap,它将为您提供选项的基础值,或者在选项为None时引发panic异常:

let origin = resp.get("origin").unwrap();

您可以使用expect自定义panic消息:
let origin = resp.get("origin").expect("Oops!");

或者使用 unwrap_or 计算默认值:

let origin = resp.get("origin").unwrap_or(&String::from("192.168.0.1"));

您还可以返回错误信息而不是抛出异常:

let origin = resp.get("origin").ok_or(Error::UnknownOrigin)?;

7

您有很多选择。

if let Some(origin) = resp.get("origin") {
    // do stuff using origin
}

origin = resp.get("origin").unwrap()
// will panic if None

resp.get("origin").map(|origin| {
    // do stuff using inner value, returning another option
})

resp.get("origin").and_then(|origin| {
    // same as map but short-circuits if there is no inner value
})

如果值为None,则mapand_then都会“短路”,如果函子本身返回一个Option,那么and_then只是map(...).flatten()的简写。 - kmdreko
@kmdreko 有点奇怪,文档为 and_then 指定了这种行为,但没有提到 map - Ivan C
另一方面,如果它不进行短路,你希望它做什么呢?如果没有值可以提供“origin”参数,它无法调用闭包... - Jmb

0

我将尝试提供一个更通用的例子来解释,因为没有任何答案或文档真正帮助我理解这个解决方案。

Option<T> 看作需要一个 if 语句来检查值是否为非空:

如果 这个东西不是 null,那么就认为它可用

为了确定它是否可用,你必须对其进行解包。

假设我们有一个名为 someone 的变量,类型为 Option<str>

match someone {
  None => {
    println!("no-one is here");
  }
  Some(name) => {
    println!("hello {name}");
  }
}
// name is not accessible here

请注意,Some 包含 Option<str> 变量的 str 值,并使其在不同的变量名 name 下可用,该变量名只能在 Some 的作用域内使用。
更清晰的编写方式可能更类似于 if else,即 if let Some:
if let Some(name) = someone {
    println!("hi {name}");
    // more logic involving name
}

希望这能帮助那些还有些困惑的人。
编辑:这里有一个可用的代码片段:https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=15817254d46340599b585b5cf7f1b0c4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接