# \[Idea/Pre-Pre-RFC\] Additional exhaustiveness checking

**URL:** <https://internals.rust-lang.org/t/idea-pre-pre-rfc-additional-exhaustiveness-checking/23972>\
**Category:** language design\
**Created:** [February 4, 2026, 7:56pm UTC](https://internals.rust-lang.org/t/idea-pre-pre-rfc-additional-exhaustiveness-checking/23972 "2026-02-04T19:56:37Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![eeleggs](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/eeleggs/32/12860_2.png) [@eeleggs](https://internals.rust-lang.org/u/eeleggs)\
**Post date:** [February 4, 2026, 7:56pm UTC](https://internals.rust-lang.org/t/idea-pre-pre-rfc-additional-exhaustiveness-checking/23972/1 "2026-02-04T19:56:37Z")

</div>

Extending the exhaustive checks feature might help writing correct programs. Exhaustive checking in the `match` statement is currently limited to input values only. Output values might also benefit from exhaustive checks. Next to the exhaustive checks, a left- or right-uniqueness check might increase program correctness. As a specific suggestion: Aside of the term `match` there should also exist an `injective_match`, a `surjective_match` and a `bijective_match`.

_Please read this as an idea. It is most likely not sound._

## Minimal mathematical foundation

When it comes to exhaustiveness guarantees, there are four properties:

- _right-uniqueness:_ Every input variable of a match has a single, unique output variable.
- _left-uniqueness:_ Every output variable has a single, unique input variable.
- _right-totality:_ Every possible output value is covered.
- _left-totality:_ Every possible input value is covered.

Currently, only the _left-totality_ exhaustive check is implemented. It is possible to switch off the check by using `_ => {}`.

## Function table

Combining these function properties, there are four common function types. As follows, these function types are named _match categories_, because `function` is a term already used in Rust. There are four binary variables `right-unique`, `left-total`, `right-total` and `left-unique`. We can ignore the `left-totality`, because default matching is a feature we wish for. Which narrows it down to three binary variables. With that, only the `right-unique` cases are relevant, which leads to four cases:

| right-unique | left-total | right-total | left-unique | **match category** |
| --- | --- | --- | --- | --- |
| Y | \* | N | N | normal `match` case, the left-totality is already a compile-time guarantee. It can be switched off, by setting the [Scrutinee](https://doc.rust-lang.org/reference/expressions/match-expr.html#grammar-Scrutinee) to `_`, which is the default matching `_ => {}`. |
| Y | \* | Y | N | named `surjective_match` or `match_onto`. |
| Y | \* | N | Y | named `injective_match`. |
| Y | \* | Y | Y | named `bijective_matching` |
| \* | \* | \* | \* | All other cases are not of relevance for Rusts `match`ing concept. |

## Example usage

The following example of a `bijective_match` indicates the use case. Additional exhaustiveness checks and what problems might occur, while implementing:

```rust
struct SymbolicNumbers { One, Two, Three }

// this matching is right-total
impl TryFrom<u32> for SymbolicNumbers {
    type Error = ();
    fn try_from(input: u32) -> Result<Self, Self::Error> {
        bijective_match input { // <- the special thing here is the `bijective_match`
            1 => Ok(SymbolicNumbers::One),
            2 => Ok(SymbolicNumbers::Two),
            3 => Ok(SymbolicNumbers::Three),
            _ => { Err(()) }
        }
    }
}

// Same code but a case is commented out.
// So this matching is not right-total. It would cause a compile-time error.
impl TryFrom<u32> for SymbolicNumbers {
    type Error = ();
    fn try_from(input: u32) -> Result<Self, Self::Error> {
        bijective_match input {
            1 => Ok(SymbolicNumbers::One),
            2 => Ok(SymbolicNumbers::Two),
            // this errors in something like:
            // error[EXXXX]: non-exhaustive right-totality patterns: `SymbolicNumbers::Three` not covered
            // because following line is missing:
            // 3 => Ok(SymbolicNumbers::Three),
            _ => Err(()),
        }
    }
}

```

### Possible problems

- What about nested `enum`s or long `tuple`s?
- What about guarded arms (containing `if` statements)?
- What about integer matching? The current `match` implementation allows specifying `Range` and Integers at the same time for `Pattern`s.
- To check the soundness of the result types (e.g. right-totality), one would have to traverse into functions and branches within the match-arm expressions. For the current `match` implementation (Pattern), there is not such a code traversal, as values have to be defined directly in the `MatchArm`.
  - [Match expressions - The Rust Reference](https://doc.rust-lang.org/reference/expressions/match-expr.html)

- In the example above (`3 => Ok(SymbolicNumbers::Three)`): instead of returning the `Ok(SymbolicNumbers::Three)` directly, a function call might return `Ok(SymbolicNumbers::Three)` instead. For instance, the body of the arm could be `3 => ok_symbolic_numbers_three()`. One could traverse into the return value, but this would open reachability problems. I would suggest to ignore the reachability issues:

```rust
fn ok_symbolic_numbers_three() -> Option<SymbolicNumbers> {
    if false {
        // 1. the compiler could traverse into all functions return instances.
        // 2. this will never be reached at runtime. 
        return Ok(SymbolicNumbers::Three);
    } else {
        // ignore this
        todo!();
    }
}

```

### Request for comments

I did not find a similar proposal or problem description. Please help me with a link, if I can not properly use the search engine of my choice. As I like the idea, I would be really happy if this turns into a discussion! 🦀

---

<div class="post-metadata">

**Author:** ![dlight](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/dlight/32/8462_2.png) [@dlight](https://internals.rust-lang.org/u/dlight)\
**Post date:** [February 4, 2026, 9:40pm UTC](https://internals.rust-lang.org/t/idea-pre-pre-rfc-additional-exhaustiveness-checking/23972/2 "2026-02-04T21:40:40Z")

</div>

There's was a RFC for that, but the proposal was dropped

> <https://github.com/rust-lang/rfcs/pull/3340>
>
> \[Rendered\](https://github.com/Xaeroxe/rfcs/blob/exhaustive-output/text/3340-exha…ustive-output.md)

I think I saw a more recent discussion on this here on IRLO, but I can't find it

About your proposal, I think that `bjective_match`, `surjective_match` and the likes.. that's too much keywords. Also I think you actually need a theorem prover (or at least something like [flux](https://github.com/flux-rs/flux)) to prove something is a bijection: naming some language construct `bijective_match` is mildly misleading because it probably could be used to create things that are not bijections.

---

<div class="post-metadata">

**Author:** ![mhaeuser](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/mhaeuser/32/13785_2.png) [@mhaeuser](https://internals.rust-lang.org/u/mhaeuser)\
**Post date:** [February 4, 2026, 10:59pm UTC](https://internals.rust-lang.org/t/idea-pre-pre-rfc-additional-exhaustiveness-checking/23972/3 "2026-02-04T22:59:25Z")

</div>

@dlight This? [Doubly-exhaustive match statement](https://internals.rust-lang.org/t/doubly-exhaustive-match-statement/23716)

---

<div class="post-metadata">

**Author:** ![ProgramCrafter](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/programcrafter/32/13494_2.png) [@ProgramCrafter](https://internals.rust-lang.org/u/ProgramCrafter)\
**Post date:** [February 9, 2026, 10:23pm UTC](https://internals.rust-lang.org/t/idea-pre-pre-rfc-additional-exhaustiveness-checking/23972/4 "2026-02-09T22:23:24Z")

</div>

> [@eeleggs](#):
>
> ```rust
> bijective_match input { // <- the special thing here is the `bijective_match`
> 1 => Ok(SymbolicNumbers::One),
> 2 => Ok(SymbolicNumbers::Two),
> 3 => Ok(SymbolicNumbers::Three),
> _ => { Err(()) }
> }
> 
> ```

It is not a bijective match. Both `SymbolicNumbers::try_from(4u32)` and `SymbolicNumbers::try_from(0u32)` return the same (i.e. structurally equal) `Err(())`.

---

<div class="post-metadata">

**Author:** ![zackw](https://sea2.discourse-cdn.com/flex002/user_avatar/internals.rust-lang.org/zackw/32/2071_2.png) [@zackw](https://internals.rust-lang.org/u/zackw)\
**Post date:** [February 11, 2026, 12:10am UTC](https://internals.rust-lang.org/t/idea-pre-pre-rfc-additional-exhaustiveness-checking/23972/5 "2026-02-11T00:10:47Z")

</div>

I'd like to reiterate here what I said in the "Doubly-exhaustive match statement" thread: a hypothetical "if this construct can be proven not to produce all possible variants of this enum, I want a compile-time error" marker is much more useful if it's not limited to `match` expressions. For example, it would be very useful to be able to apply it to `FromStr` impls for enums, whether or not the body of the function definition involves `match` at all.
