Skip to content

Proposal: Add inspect and inspect_err methods to Result, and inspect method to Option #3190

@cyqsimon

Description

@cyqsimon

When you have a function call that returns a Result type, it's a very common need to perform some immutable action using one of its contained values (such as printing an additional message), and then pass on the Result for other uses. I guess this is best described as the inspect operation of a wrapper type, similar to Iterator::inspect in its purpose.

Consider this pseudocode snippet:

1. Take a path, treat it as a Unix socket, and bind a listener to it
2. If error, print out a custom message, and propagate error
3. Do other things with the listener
4. Return if all successful

Ideally, I would want to write this:

fn start_listener<P: AsRef<Path>>(bind_addr: P) -> io::Result<()> {
    let listener = UnixListener::bind(&bind_addr)
        .inspect_err(|err| println!("Listener cannot bind: {}", err))?;

    // do things with listener
    Ok(())
}

Currently, we could write this code:

fn start_listener<P: AsRef<Path>>(bind_addr: P) -> io::Result<()> {
    let bind_res = UnixListener::bind(&bind_addr);
    if let Err(err) = &bind_res {
        println!("Listener cannot bind: {}", err);
    }

    let listener = bind_res?;
    // do things with listener
    Ok(())
}

Or this:

fn start_listener<P: AsRef<Path>>(bind_addr: P) -> io::Result<()> {
    let listener = match UnixListener::bind(&bind_addr) {
        ok_val @ Ok(_) => ok_val,
        Err(err) => {
            println!("Listener cannot bind: {}", err);
            Err(err)
        }
    }?;

    // do things with listener
    Ok(())
}

Or this:

fn start_listener<P: AsRef<Path>>(bind_addr: P) -> io::Result<()> {
    let listener = UnixListener::bind(&bind_addr).map_err(|err| {
        println!("Listener cannot bind: {}", err);
        err
    })?;

    // do things with listener
    Ok(())
}

The first two options are verbose; the third is overkill because we don't actually need to map in this case, so there's no need to take ownership of err. If we introduce an inspect_err method, it would make the code more concise, but more importantly it delivers the intent much more clearly. Also bear in mind that this is a very simple example - in many use cases method calls on Result and Option are often chained. In those situations the small ergonomics improvements would be more pronounced.

The proposed Result::inspect would have the signature:

pub fn inspect<F>(self, op: F) -> Self where F: Fn(&T) -> (),

Result::inspect_err would have the signature:

pub fn inspect_err<F>(self, op: F) -> Self where F: Fn(&E) -> (),

In the same spirit, Option::inspect would have the signature:

pub fn inspect<F>(self, op: F) -> Self where F: Fn(&T) -> (),

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions