rust-lang / rust-lang/reference

Document pattern evaluation order

Open
#1,665 4 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-patterns
Dominant language
Rust
Stars
1.6k
Forks
607
PR merge metrics
PR metrics pending

Description

For most patterns and in safe code, "evaluation"(/matching?) order of subpatterns does not matter, but there is (that I can think of) one instance on stable where pattern evaluation order matters: matching on a struct with a tag and a union field (and similar situations).

The example in the section on struct patterns explicitly states that field order does not matter, but it also does not have any patterns where the evaluation order of subpatterns would matter.

match s {
    Point {x: 10, y: 20} => (),
    Point {y: 10, x: 20} => (),    // order doesn't matter
    Point {x: 10, ..} => (),
    Point {..} => (),
}

The section on unions does mention pattern matching, but does not say anything about pattern evaluation order. It gives an example of pattern-matching on a manual tagged union, though pattern evaluation order does not matter for the example given[^2]. In a slightly different example, however, the field order does matter:

the example
#[derive(Clone, Copy)]
enum Tag { A, B, }

#[derive(Clone, Copy)]
#[repr(C)]
union Value {
    a: u32,
    b: u8, // note that b is smaller than a
}

/// Assume that if tag == Tag::A, then val.a is valid, and if tag == Tag::B, then tag.b is valid.
#[derive(Clone, Copy)]
struct Tagged {
    tag: Tag,
    val: Value,
}

unsafe fn tag_first(v: Tagged) -> bool {
    match v {
        // fine under miri with tag == B, sees that `tag != A` and skips the arm
        Tagged { tag: Tag::A, val: Value { a: 0 } } => true,
        _ => false,
    }
}
unsafe fn val_first(v: Tagged) -> bool {
    match v {
        // error under miri with tag == B, since it reads the padding bytes after `Value::b`
        Tagged { val: Value { a: 0 }, tag: Tag::A } => true,
        _ => false,
    }
}

fn main() {
    let v = Tagged {
        tag: Tag::B,
        val: Value { b: 0 },
    };
    unsafe {
        tag_first(v);
        val_first(v);
    }
}

For unstable code, I suppose deref_patterns might also make it important to document pattern evaluation order, or maybe that feature is/will be restricted enough for it not to matter. Depending on the resolution of https://github.com/rust-lang/unsafe-code-guidelines/issues/412 pattern evaluation order might be important if matching on references-to-references-to-invalid-data (miri example)?

I'm not sure if this is fully the intended behavior[^1], or if it is intended, how best to document it.

[^1]: Alternately, instead of documenting pattern evaluation order, it could be specified that if any (union) field used in a pattern match is invalid/uninitialized, then the whole arm is UB, regardless of the order the fields were written in the pattern.

[^2]: in that example, the union field is fully-initailized either way, or UB happens regardless of pattern evaluation order

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the Reference's struct patterns section and the unions section on pattern matching, then compare the linked stable and Miri examples. Determine whether pattern evaluation order is intended behavior or whether invalid union fields make the whole match arm undefined behavior. Done means the intended rule and its consequences are clearly specified in the relevant documentation.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
documentation
Issue type
Documentation
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.