How to convert Option<Option<String>> to Option<Option<&str>> in Rust?
Asked Answered
Z

2

5

I am trying to make this conversion (Option<Option<String>> to Option<Option<&str>>) possible, but I still failed after trying many methods including using .map.

I know that the conversion is possible, however, if there's no nested Option (i.e. Option<String> to Option<&str>): just using .as_deref().
Using .map(|inner| inner.as_deref()) violates the ownership.

Zone answered 6/12, 2023 at 1:24 Comment(0)
L
8

The reason using a plain map violates ownership rules is because it takes self. You're consuming the outer Option and then trying to borrow something from inside it, which doesn't work.

However, there's an easy approach here, using as_ref and then as_deref inside the map. as_ref turns &Option<T> into Option<&T>, where T is Option<String>, so when you call map, you get an &Option<String>, which you can then safely pass to as_deref.

fn main() {
    let foo = Some(Some(String::from("abc")));
    let bar: Option<Option<&str>> = foo.as_ref().map(|r| r.as_deref());
    eprintln!("{:?}", bar);
}
Langham answered 6/12, 2023 at 1:35 Comment(1)
That is a neat solution. Thanks!Zone
G
5

When you find yourself stringing together multiple methods and closures to work with Option, Result, or even things like hash_map::Entry, consider going back to basics and using a match:

pub fn opt_opt_str_ref(input: &Option<Option<String>>) -> Option<Option<&str>> {
    match input {
        Some(Some(s)) => Some(Some(s.as_str())),
        Some(None) => Some(None),
        None => None,
    }
}

This has the advantage that when read, its meaning is obvious; there is no question about what it actually does. If it needs to be changed to do something slightly different, how to do so is obvious.

It's not always better (and this particular example is particularly inelegant with all the repetition), but (in my opinion) it's always worth considering the option.

Gibrian answered 6/12, 2023 at 5:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.