Always move by ref into pattern guard


#1

I have the following code that ignores NotFound errors.

/// Map not found error to ok
fn allow_not_found(input: io::Result<()>) -> io::Result<()> {
    match input {
        Ok(()) => Ok(()),
        Err(e) if e.kind() == io::ErrorKind::NotFound => Ok(()),
        Err(e) => Err(e),
    }
}

If I try to compile the second line of the match without the ref e (as shown), I get a build error (cannot bind by-move into a pattern guard).

With the changes around not requiring ref, maybe instead of failing to compile the above example could just default to ref in the guard, even though the match itself is a move match?

Would there be and problems with this approach I haven’t thought of?

Only things that might be a problem off the top of my head:

  • Copy types

#2

I think this is even more confusing than match ergonomics. In that case at least you can make the point that from a reference, you are getting back a reference, even if not in an entirely obvious way. Here, the root value is moved, so there’s little sense in defaulting the binding to a reference without ref. Doing so would also make refactoring harder because you would need to remove/add back the ref as you move the guard around.

By the way, in this very case, you can just rewrite the match without any guards:

/// Map not found error to ok
fn allow_not_found(input: io::Result<()>) -> io::Result<()> {
    match input {
        Ok(()) => Ok(()),
        Err(e) => if e.kind() == io::ErrorKind::NotFound { Ok(()) } else { Err(e) },
    }
}

#3

What I’m saying is that the guard would get a reference, but the matching block would still move, so

  • match value case:
    • Reference in guard
    • Move in match
  • match &value case:
    • Reference in guard
    • Reference in match

#5

Sure, I understand that – regardless of where the bound name (e) is a reference, it’s still somewhat confusing. (Actually, if it’s a reference in the guard but a value outside, then the same name in the same scope has two different types – I certainly wouldn’t expect that.)


#6

I agree with you, but I thought I’d mention it because it kinda matches what’s being talked about w.r.t. the Rust 2018 stuff.


#7

If the capture in the pattern guard is only used by-ref, it makes sense that it should work to me. Closures already have a similar behavior on captures, so it wouldn’t be introducing a new kind of inference.

I think the majority of cases where this is hit are by-ref method calls. There’s no real reason this shouldn’t work that I see.

The real solution should obviously be more detailed than just using by-ref to capture into pattern guards, but it should be possible to match by move conditioned on a by-ref check. It can’t always be inlined into a match arm either.