How can I pull data out of an Option for independent use?
Asked Answered
C

4

26

Is there a way to 'pull' data out of an Option? I have an API call that returns Some(HashMap). I want to use the HashMap as if it weren't inside Some and play with the data.

Based on what I've read, it looks like Some(...) is only good for match comparisons and some built-in functions.

Simple API call pulled from crate docs:

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

Result:

Some("75.69.138.107")
Cletis answered 25/11, 2020 at 0:44 Comment(1)
Some examples are fully documented here: doc.rust-lang.org/rust-by-example/error/option_unwrap.htmlPhysique
V
32
if let Some(origin) = resp.get("origin") {
    // use origin
}

If you can guarantee that it's impossible for the value to be None, then you can use:

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

Or:

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

And, since your function returns a Result:

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

Or with a custom error type:

let origin = resp.get("origin").ok_or(MyError::DoesntExist)?;
Valse answered 25/11, 2020 at 0:55 Comment(0)
I
11

The most common way is with if let:

if let Some(origin) = resp.get("origin") {
    origin.do_stuff()
}

For more fine grained control, you can use pattern matching:

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

You could also use unwrap, which will give you the underlying value of the option, or panic if it is None:

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

You can customize the panic message with expect:

let origin = resp.get("origin").expect("Oops!");

Or compute a default value with unwrap_or:

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

You can also return an error instead of panicking:

let origin = resp.get("origin").ok_or(Error::UnknownOrigin)?;
Impearl answered 25/11, 2020 at 1:34 Comment(0)
P
8

Your options are a plenty.

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
})
Precentor answered 25/11, 2020 at 1:1 Comment(3)
both map and and_then "short-circuit" if the value is None, and_then is just a shorthand for map(...).flatten() if the functor itself returns an OptionVoronezh
@Voronezh A bit odd that the docs specify this behaviour for and_then but don't mention it for mapPrecentor
OTOH, what do you expect it to do if it does not short-circuit? It can't call the closure if there is no value to give the origin argument…Titus
B
0

I'll try and provide an explanation here with a more generic example as none of the answers or documentation really helped me understand the solution.

Think of Option<T> as requiring an if statement to check if the value is not null:

if this thing is not null, then consider it usable

In order to determine if it is usable you have to unwrap it.

Assume we have an Option<str> with the variable name someone:

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

Note that Some contains the str value of the Option<str> variable, and makes it usable under a different variable name - name - which can only be used inside the scope of Some.

A cleaner way of writing it which might be more similar to if else is if let Some:

if let Some(name) = someone {
    println!("hi {name}");
    // more logic involving name
}

Hopefully this helps some people who are still a bit confused.

Edit: here is a working gist: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=15817254d46340599b585b5cf7f1b0c4

Barilla answered 11/5, 2023 at 21:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.