How to avoid layers of repetition of `else { None }` with extracting values

Hi all,

I'm a hobbyist that is slowly going through the proc macro workshop, just for fun.

I have the first handful of tests on the Builder project working, but I keep running into a pattern that makes me think that there is a more idiomatic alternative that I'm missing: setting an Optional<T> with layers of match and if let that result in layers and layers of else { None }.

For example. (or the most recent version, with else { Ok(None) } repeated instead.)

It seems that if trying to match target == 42 that is embedded several layers deep in some struct with mixed Optionals and sequences etc. on the way down that there is probably a better way to do several matches without having to keep saying else { None } for every alternative path. Right?

I keep thinking that there is probably some way to .map or .and_then my way to a cleaner approach, but the sequences and match guards seem to keep interfering with chaining them, and AFAICT there's no way around that.

TIA for any suggestions!

1 Like

One way (that I can think of now at midnight) I've handled those cases is by writing a fallible closure inside the infallible function to able to use the ? operator (this is an example from my recent answer to a thread on r/learnrust which is why I remember it) :

fn extract_name(path: &Path) -> &str {
    static PATTERN: Lazy<Regex> =
        Lazy::new(|| Regex::new(...).unwrap());
    // this closure is fallible, so it can make use of `?`
    let capture = || {
        let stem = path.file_stem()?.to_str()?;
        let caps = PATTERN.captures(stem)?;
        Some(caps.get(1)?.as_str())
    };
    capture().unwrap_or("untitled")
}

I've also done it here for something public a bit a more involved. I think I've first seen that last year in a tweet ? It probably should belong in Rust Patterns.

3 Likes

Thanks for the suggestion! I was about to propose something like this but you beat me to it.

For some reason I thought it would need to be a standalone function, but a closure would be just dandy.

EDIT: Although on stable I guess the ? operator won't really work for Optional unless I convert the None (I think there is a NoneError coming in unstable, right?).

Perhaps I could make something like a closure with early returns for the happy path and just a None at the end, and add some semicolons to have all the other paths return () everywhere else?

This seems a little better: proc-macro-workshop/lib.rs at 6cdef09038dc98020db892317e692780793e1096 · n8henrie/proc-macro-workshop · GitHub

The NoneError is only relevant if the closure returns a Result, but the ? is used on an Option. If you use Option in both places, it should work.

1 Like

Wow, my mind is blown. It looks my first commit of Rust code was March 21, 2019. That means it's been two years that I could have been using ? on Option and somehow didn't figure that out. That makes for a lot of totally unnecessary usages of <Option<T>>.ok_or_else(|| LetMeUseQuestionmarkError)?!

How could I have missed that. :man_facepalming:

Thank you. That makes the path of refactoring this into a closure or function to leverage early returns much more obvious.

2 Likes

A little more refactoring: proc-macro-workshop/lib.rs at 97bf530cf042fd351b37fda3b9c113aee483f589 · n8henrie/proc-macro-workshop · GitHub

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.