Option::on_none()

Similarly to Result::inspect_err() it could be worth having Option::on_none. This can be especially useful for logging in chained expressions:

    pub fn load() -> Option<Self> {
        Self::file_path()
            .or_else(|| {
                warn!("User configuration directory not found.");
                None
            })
            .and_then(|filename| {
                File::open(&filename)
                    .inspect_err(|error| {
                        if error.kind() != ErrorKind::NotFound {
                            error!("Could not read user configuration file: {error}");
                        }
                    })
                    .ok()
                    .and_then(|file| {
                        read_to_string(file)
                            .inspect_err(|error| {
                                error!("Could not read user configuration file: {error}");
                            })
                            .ok()
                            .and_then(|text| {
                                serde_json::from_str(&text)
                                    .inspect_err(|error| {
                                        error!("Malformed user configuration file: {error}");
                                    })
                                    .ok()
                            })
                    })
            })
    }

It would be a bit more ergonomic, to not having to return the None:

    pub fn load() -> Option<Self> {
        Self::file_path()
            .on_none(|| warn!("User configuration directory not found."))
            .and_then(|filename| {
                File::open(&filename)
                    .inspect_err(|error| {
                        if error.kind() != ErrorKind::NotFound {
                            error!("Could not read user configuration file: {error}");
                        }
                    })
                    .ok()
                    .and_then(|file| {
                        read_to_string(file)
                            .inspect_err(|error| {
                                error!("Could not read user configuration file: {error}");
                            })
                            .ok()
                            .and_then(|text| {
                                serde_json::from_str(&text)
                                    .inspect_err(|error| {
                                        error!("Malformed user configuration file: {error}");
                                    })
                                    .ok()
                            })
                    })
            })
    }
1 Like

Why not call it inspect_none(), for consistency with existing methods?

Anyway, note that it was already tried at Add `Option::inspect_none` & minor example improvements to other new `inspect` methods by cyqsimon · Pull Request #94317 · rust-lang/rust · GitHub, so any attempt will have to resolve the concerns raised there.

2 Likes

TBH, that this make me think is not "oh, there should be an inspect_none" but rather "this looks like a great place for let else".

4 Likes
    .ok_or_else(|| warn!("User configuration directory not found."))
    .ok()

(Just kidding, that's awful.)

2 Likes

Using hypothetical function-level try, yeet[1], let-else-match and a little bit of over-cleverness[2], the example could be written as:

pub fn load() -> Option<Self> try {
    let Some(filename) = Self::file_path() else {
        yeet warn!("User configuration directory not found.");
    };
    let Ok(text) = fs::read_to_string(filename) else match {
        Err(err) => yeet if err.kind() != ErrorKind::NotFound {
            error!("Could not read user configuration file: {err}");
        },
    };
    let Ok(this) = serde_json::from_str(&text) else match {
        Err(err) => yeet error!("Malformed user configuration file: {err}"),
    };
    this
}

Desugar the used hypothetical features as desired. Much cleaner than the tower of combinators, IMHO.


  1. It's the accepted placeholder syntax; no complaining allowed (please and thank you). ↩︎

  2. In Option contexts, yeet; acts like None?. Except, since it's actually yeet (); (like how return in fn -> () works), yeet unit_returning_expr(); also will work. Clippy will yell at you for doing this, though. (Let me Ok(f()?) in fn -> Result<()>, that's much better than writing out f().map_err(From::from).) ↩︎

7 Likes

That was my first thought as well. However, since it is None and the closure receives nothing, there is nothing to inspect. Hence, I found on_none more fitting. Regarding the PR, I did not see this. I only searched the forums here for whether this has already been proposed, but found nothing.

This topic was automatically closed 540 days after the last reply. New replies are no longer allowed.