惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Vulnerabilities – Threatpost
T
The Blog of Author Tim Ferriss
S
SegmentFault 最新的问题
D
DataBreaches.Net
博客园_首页
罗磊的独立博客
B
Blog
T
Threat Research - Cisco Blogs
C
Cisco Blogs
GbyAI
GbyAI
Engineering at Meta
Engineering at Meta
WordPress大学
WordPress大学
G
GRAHAM CLULEY
H
Help Net Security
酷 壳 – CoolShell
酷 壳 – CoolShell
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
爱范儿
爱范儿
SecWiki News
SecWiki News
T
Threatpost
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Schneier on Security
Schneier on Security
T
The Exploit Database - CXSecurity.com
Google Online Security Blog
Google Online Security Blog
T
Tor Project blog
小众软件
小众软件
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Y
Y Combinator Blog
H
Hacker News: Front Page
V
V2EX
Security Latest
Security Latest
Cloudbric
Cloudbric
Simon Willison's Weblog
Simon Willison's Weblog
Attack and Defense Labs
Attack and Defense Labs
D
Darknet – Hacking Tools, Hacker News & Cyber Security
P
Proofpoint News Feed
博客园 - 三生石上(FineUI控件)
NISL@THU
NISL@THU
S
Secure Thoughts
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
V2EX - 技术
V2EX - 技术
Vercel News
Vercel News
P
Palo Alto Networks Blog
IT之家
IT之家
MyScale Blog
MyScale Blog
有赞技术团队
有赞技术团队
Application and Cybersecurity Blog
Application and Cybersecurity Blog
D
Docker
Google DeepMind News
Google DeepMind News
Webroot Blog
Webroot Blog

Rust Blog

Security Advisory for Cargo (CVE-2026-5223) | Rust Blog Security Advisory for Cargo (CVE-2026-5222) | Rust Blog Project goals update — April 2026 (end of 2025H2) | Rust Blog Rust is participating in Outreachy | Rust Blog Raising the baseline for the `nvptx64-nvidia-cuda` target | Rust Blog Announcing Google Summer of Code 2026 selected projects | Rust Blog Announcing Rust 1.95.0 | Rust Blog docs.rs: building fewer targets by default | Rust Blog Changes to WebAssembly targets and handling undefined symbols | Rust Blog Announcing Rust 1.94.1 | Rust Blog Security advisory for Cargo | Rust Blog What we heard about Rust's challenges | Rust Blog Call for Testing: Build Dir Layout v2 | Rust Blog Announcing rustup 1.29.0 | Rust Blog Announcing Rust 1.94.0 | Rust Blog 2025 State of Rust Survey Results | Rust Blog Rust debugging survey 2026 | Rust Blog Update on the October 15, 2018 incident on crates.io Announcing Rust 1.29.2 Announcing Rust 1.29 Announcing Rust 1.28 What is Rust 2018? Announcing Rust 1.27.2 Announcing Rust 1.27.1 Security Advisory for rustdoc Announcing Rust 1.27 Announcing Rust 1.26.2 Announcing Rust 1.26.1 Rust turns three Announcing Rust 1.26 The Rust Team All Hands in Berlin: a Recap Increasing Rust’s Reach 2018 Announcing Rust 1.25 Rust's 2018 roadmap Announcing Rust 1.24.1 Announcing Rust 1.24 The 2018 Rust Event Lineup Announcing Rust 1.23 New Year's Rust: A Call for Community Blogposts Rust in 2017: what we achieved Announcing Rust 1.22 (and 1.22.1) Fearless Concurrency in Firefox Quantum Announcing Rust 1.21 impl Future for Rust Rust 2017 Survey Results Announcing Rust 1.20 Announcing Rust 1.19 The 2017 Rust Conference Lineup Rust's 2017 roadmap, six months in Increasing Rust’s Reach Announcing Rust 1.18 Two years of Rust The Rust Libz Blitz Launching the 2017 State of Rust Survey Announcing Rust 1.17 Announcing Rust 1.16 Rust's language ergonomics initiative Announcing Rust 1.15.1 Rust's 2017 roadmap Announcing Rust 1.15 Announcing Rust 1.14 Announcing the First Underhanded Rust Contest Announcing Rust 1.13 Announcing Rust 1.12.1 Announcing Rust 1.12 Incremental Compilation Announcing Rust 1.11 Shape of errors to come The 2016 Rust Conference Lineup Announcing Rust 1.10 State of Rust Survey 2016 Announcing Rust 1.9 One year of Rust Taking Rust everywhere with rustup Launching the 2016 State of Rust Survey Cargo: predictable dependency management Introducing MIR Announcing Rust 1.8 Announcing Rust 1.7 Announcing Rust 1.6 Announcing Rust 1.5 Announcing Rust 1.4 Announcing Rust 1.3 Rust in 2016 Announcing Rust 1.2 Rust 1.1 stable, the Community Subteam, and RustCamp Announcing Rust 1.0 Abstraction without overhead: traits in Rust Rust Once, Run Everywhere Mixing matching, mutation, and moves in Rust Fearless Concurrency with Rust Announcing Rust 1.0 Beta Announcing Rust 1.0.0.alpha.2 Rust 1.0: status report and final timeline Announcing Rust 1.0 Alpha Rust 1.0: Scheduling the trains Yehuda Katz and Steve Klabnik are joining the Rust Core Team Cargo: Rust's community crate host Stability as a Deliverable Road to Rust 1.0
Recent and future pattern matching improvements | Inside Rust Blog
Mazdak "Centril" Farrokhzad on behalf of the language team · 2020-03-04 · via Rust Blog

Much of writing software revolves around checking if some data has some shape ("pattern"), extracting information from it, and then reacting if there was a match. To facilitate this, many modern languages, Rust included, support what is known as "pattern matching".

If you are new to Rust or want to refresh your knowledge, you may first want to read chapters 6, Enums and Pattern Matching and 18, Patterns and Matching in the book, or read more about match expressions and patterns in the reference.

Pattern matching in Rust works by checking if a place in memory (the "data") matches a certain pattern. In this post, we will look at some recent improvements to patterns soon available in stable Rust as well as some more already available in nightly.

If you are familiar with the nightly features discussed and would like to help out with the efforts to drive them to stable, jump ahead to *How can I help?.

Subslice patterns, [head, tail @ ..]

Lists are one of the most basic and common data structures found in software. In Rust, lists are usually a contiguous sequence of elements in memory, or a slice.

Since slices are so commonplace, it is important that working with them is easy. To that end, we stabilized fixed-length slice patterns in Rust 1.26.0. So now it is possible to e.g., write let [a, b, c] = my_array; to destructure an array of 3 elements. Oftentimes, however, we're working with a slice of unknown length, so given only fixed-length slice patterns, we have to provide a fallback match arm with e.g. _ as the pattern.

In Rust 1.42.0, we are stabilizing subslice patterns. To introduce a subslice pattern, we use .. which denotes a variable-length gap, matching as many elements as possible not matched by the patterns before and after the ... For example, in a parser, we would like to error when a list of attributes, attrs, is not followed by an item, so we write:

/// Recover if we parsed attributes and expected an item but there was none.
fn recover_attrs_no_item(&mut self, attrs: &[Attribute]) -> PResult<'a, ()> {
    let (start, end) = match attrs {
        [] => return Ok(()),
        [x0] => (x0, x0),
        [x0, .., xn] => (x0, xn),
    };
    let msg = if end.is_doc_comment() {
        "expected item after doc comment"
    } else {
        "expected item after attributes"
    };
    let mut err = self.struct_span_err(end.span, msg);
    if end.is_doc_comment() {
        err.span_label(end.span, "this doc comment doesn't document anything");
    }
    if let [.., penultimate, _] = attrs {
        err.span_label(start.span.to(penultimate.span), "other attributes here");
    }
    Err(err)
}

Here we have two subslice patterns, the first one being [x0, .., xn]. In this case, the pattern binds x0, the first element, and xn, the last element, and ignores everything in the middle, matching a slice with at least two elements in total. Meanwhile, [] and [x0] match cases with fewer than two elements, so the compiler knows that we have covered all possibilities. In the latter case, we extract the penultimate element of the slice, which, as the name suggests, also requires that the slice has at least two elements.

We can also bind a subslice to a variable. For example, suppose we want to disallow ... in all but the last parameter of a function. If so, we can write:

match &*fn_decl.inputs {
    ... // other arms
    [ps @ .., _] => {
        for Param { ty, span, .. } in ps {
            if let TyKind::CVarArgs = ty.kind {
                self.err_handler().span_err(
                    *span,
                    "`...` must be the last argument of a C-variadic function",
                );
            }
        }
    }
}

Here, ps @ .. will bind the initial elements of the slice to ps and ignore the last element.

After more than 7 years of baking in nightly, with many twists and turns, subslice patterns will finally be stable. To get here, we've had to redesign the feature, plug soundness holes in the borrow checker, and substantially refactor the exhaustiveness checker. For more on how we got here, read the stabilization report, Thomas Hartmann's blog post, and stay tuned for the 1.42.0 release announcement on the 12th of March.

Nested OR-patterns

When pattern matching on an enum, the logic for some of the variants may be exactly the same. To avoid repeating ourselves, the | separator in match, if let, or while let expressions can be used to say that the branch should be taken if any of the |-separated patterns match. For example, we may write:

// Any local node that may call something in its body block should be explored.
fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
    match tcx.hir().find(hir_id) {
        Some(Node::Item(..))
        | Some(Node::ImplItem(..))
        | Some(Node::ForeignItem(..))
        | Some(Node::TraitItem(..))
        | Some(Node::Variant(..))
        | Some(Node::AnonConst(..))
        | Some(Node::Pat(..)) => true,
        _ => false,
    }
}

This is serviceable, but Some(_) is still repeated several times. With #![feature(or_patterns)], which recently became usable on nightly, this repetition can be avoided:

// Any local node that may call something in its body block should be explored.
fn should_explore(tcx: TyCtxt<'_>, hir_id: hir::HirId) -> bool {
    match tcx.hir().find(hir_id) {
        Some(
            Node::Item(..)
            | Node::ImplItem(..)
            | Node::ForeignItem(..)
            | Node::TraitItem(..)
            | Node::Variant(..)
            | Node::AnonConst(..)
            | Node::Pat(..),
        ) => true,
        _ => false,
    }
}

Previously, when using | in a match expression, the | syntax was part of match itself. With or_patterns, this is now part of patterns themselves, so you can nest OR-patterns arbitrarily, and use them in let statements too:

let Ok(x) | Err(x) = foo();

An OR-pattern covers the union of all the |-ed ("or-ed") patterns. To ensure that whatever alternative matched, all bindings are consistent and initialized, each or-ed pattern must include the exact same set of bindings, with the same types, and the same binding modes.

Bindings after @

When matching on a certain substructure, you sometimes want to hold on to the whole. For example, given Some(Expr { .. }), you would like to bind the outer Some(_) layer. In Rust, this can be done using e.g., expr @ Some(Expr { .. }), which binds the matched place to expr while also ensuring that it matches Some(Expr { .. }).

Suppose also that Expr has a field span that you would also use. In ancient times, that is before Rust 1.0, this was possible, but today, it results in an error:

error[E0303]: pattern bindings are not allowed after an `@`
 --> src/lib.rs:L:C
  |
L |         bar @ Some(Expr { span }) => {}
  |                           ^^^^ not allowed after `@`

This was turned into an error in #16053, mainly due to the difficulties of encoding borrow checking rules in a sound way in the old AST based borrow checker.

Since then, we have removed the old borrow checker in favor of one based on MIR, which is a simpler, and more appropriate data structure for borrow checking. Specifically, in the case of a statement like let ref x @ ref y = a;, we would get roughly the same MIR as if we had used let x = &a; let y = &a;.

So now that having bindings to the right of @ is handled uniformly and correctly by the borrow checker (e.g., the compiler won't allow ref x @ ref mut y), we have decided to allow them under #![feature(bindings_after_at)], now available on nightly. With the feature gate enabled, you may for example write:

#![feature(bindings_after_at)]

fn main() {
    if let x @ Some(y) = Some(0) {
        dbg!(x, y);
    }
}

Our hope is that with providing this feature, we remove one surprising corner of the language.

Combining by-move and by-ref bindings

For similar reasons as noted in the case of bindings after @, Rust does not currently allow you to combine normal by-move bindings with those that are by-ref. For example, should you write...:

fn main() {
    let tup = ("foo".to_string(), 0);
    let (x, ref y) = tup;
}

... you would get an error:

error[E0009]: cannot bind by-move and by-ref in the same pattern
 --> src/main.rs:3:10
  |
3 |     let (x, ref y) = tup;
  |          ^  ----- by-ref pattern here
  |          |
  |          by-move pattern here

At the same time, however, the compiler is perfectly happy to allow...:

fn main() {
    let tup = ("foo".to_string(), 0);
    let x = tup.0;
    let ref y = tup.1;
}

... even though there is no semantic difference between these programs.

Now that we have moved to the new borrow checker, as outlined in the previous section, we have relaxed this restriction on nightly as well, so under #![feature(move_ref_pattern)], you may write:

#![feature(move_ref_pattern)]

fn main() {
    let tup = ("foo".to_string(), 0);
    let (x, ref y) = tup;
}

How can I help?

To recap, we have three unstable features, all improving pattern matching in different ways:

To help us transition these features over to stable Rust, we need your help to ensure that they meet the expected quality standards. To help out, consider:

  • Using the features in your code where applicable, if a nightly compiler is something you are OK with, and reporting any bugs, problems, deficiencies in diagnostics, etc. as issues.
  • Looking through the reported issues under the feature gate labels (e.g., F-or_patterns) and seeing if you can help out with any of them.
    • In particular, if you can help out with writing tests, that is appreciated.

Thanks for reading, and happy pattern matching!