Skip to content

Commit 0fa1907

Browse files
committed
docs: Add conceptual guides for Rust pattern matching and let-chains
- Restructured the "Let-Chains" documentation into a Diataxis format for clarity and coherence. - Created a new page providing an overview of pattern matching, detailing its principles, mechanisms, and examples across various programming languages.
1 parent 64a5589 commit 0fa1907

File tree

3 files changed

+230
-0
lines changed

3 files changed

+230
-0
lines changed

journals/2025_12_03.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
- [[Rust/Pattern Matching/Let-Chains]] restructured into Diataxis conceptual guide format
2+
- [[Programming/Concept/Pattern Matching]] created as conceptual overview of pattern matching
3+
- Today I learned about the term `SCREAMING_SNAKE_CASE`. [[LOL]]
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
tags:: [[Programming]], [[Diataxis/Explanation]]
2+
3+
- # Pattern Matching Conceptual Overview
4+
- ## Overview
5+
- Pattern matching is a fundamental programming concept that enables the examination and deconstruction of data structures based on their form
6+
- Allows developers to specify patterns to which data should conform
7+
- Facilitates concise and readable code for data extraction, transformation, and control flow
8+
- ## Context
9+
- **Early Implementations**: Languages like SNOBOL (1962) and COMIT (1957) were among the first to introduce pattern matching, primarily for string manipulation
10+
- **Functional Programming**: The ML language (1973) and its successors integrated pattern matching as a core feature
11+
- **Modern Developments**: Many modern languages have incorporated pattern matching to provide safer and more expressive code constructs
12+
- Pattern matching addresses the need for type-safe, expressive ways to handle different data variants and structures
13+
- ## Key Principles
14+
- **Exact matching**: Unlike pattern recognition, which may allow approximate matches, pattern matching typically requires an exact match
15+
- **Structural decomposition**: Patterns can take the form of sequences or tree structures, enabling destructuring of complex data
16+
- **Type safety**: Pattern matching enables compile-time checking of exhaustive handling of all possible cases
17+
- **Control flow**: Pattern matching directs program execution based on data structure, replacing verbose if-else chains
18+
- ## Mechanism
19+
- Pattern matching involves checking a given sequence of tokens or data structures for the presence of constituents of some pattern
20+
- When a pattern matches, variables can be bound to matched components
21+
- Patterns can match on:
22+
- Data structure shape (tuples, structs, enums)
23+
- Value equality
24+
- Type variants
25+
- Nested structures
26+
- Most implementations support:
27+
- **Locating patterns within data**: Finding specific structures or values
28+
- **Extracting components**: Destructuring data structures to access their parts
29+
- **Substituting matching patterns**: Replacing matched patterns with other sequences
30+
- **Control flow**: Directing program execution based on data structure
31+
- ## Examples
32+
- ### Functional Languages
33+
- **Haskell, ML, Scala**: Use pattern matching extensively to destructure data types and control program flow
34+
- Pattern matching is often used in function definitions
35+
- Example (Haskell):
36+
- ~~~haskell
37+
factorial 0 = 1
38+
factorial n = n * factorial (n - 1)
39+
~~~
40+
- ### Object-Oriented Languages
41+
- **C#**: Introduced pattern matching in C# 7.0 for switch statements and expressions
42+
- **Java**: Added pattern matching features to enhance type checking and data extraction
43+
- ### Systems Languages
44+
- **Rust**: Uses pattern matching extensively with `match` expressions for safe data handling
45+
- **Swift**: Incorporates pattern matching for safer and more expressive code constructs
46+
- ### Scripting Languages
47+
- **Python**: Python 3.10 introduced structural pattern matching with `match`/`case` statements
48+
- Example (Python):
49+
- ~~~python
50+
match command:
51+
case ["move", x, y]:
52+
move_to(x, y)
53+
case ["draw", shape]:
54+
draw_shape(shape)
55+
case _:
56+
print("Unknown command")
57+
~~~
58+
- ## Common Use Cases
59+
- **Destructuring**: Extracting values from complex data structures (tuples, structs, enums)
60+
- **Type checking**: Handling different types or variants in a type-safe manner
61+
- **Control flow**: Replacing if-else chains or switch statements with more expressive pattern-based logic
62+
- **Error handling**: Matching on error types and success cases
63+
- **Data transformation**: Converting between different data representations
64+
- ## Misconceptions
65+
- Pattern matching is only for functional languages → **False**. Many modern languages across paradigms support pattern matching
66+
- Pattern matching is just a fancy switch statement → **False**. Pattern matching provides structural decomposition and type safety that goes beyond traditional switches
67+
- Pattern matching requires exact string matches → **False**. Pattern matching works on data structures, types, and values, not just strings
68+
- ## Related
69+
- [[Programming/Pattern Matching]] - General pattern matching reference
70+
- [[Rust/match]] - Pattern matching in Rust using `match` expressions
71+
- [[Rust/Pattern Matching/Let-Chains]] - Rust's let-chains feature for pattern matching in conditions
72+
- [[Programming/Enum]] - Enums are often used with pattern matching
73+
- [[Programming/Destructuring]] - Related concept of extracting values from structures
74+
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
tags:: [[Rust]], [[Programming]], [[Diataxis/Explanation]]
2+
3+
- # Rust Let-Chains Conceptual Overview
4+
- ## Overview
5+
- Let-chains are a Rust feature that allows pattern matching expressions to be chained together with boolean operators (`&&`, `||`) within `if` and `while` condition expressions
6+
- Stabilized in Rust 1.64, let-chains enable combining pattern matching, variable binding, and boolean logic in a single linear condition
7+
- They transform deeply nested `if let` statements into readable, linear chains that short-circuit like normal boolean expressions
8+
- Let-chains fuse pattern matching, conditional binding, boolean short-circuiting, scope propagation, and expression-oriented programming into one construct
9+
- ## Context
10+
- Before let-chains, handling multiple `Option` or `Result` values required deeply nested `if let` statements, leading to "pyramid of doom" code structures
11+
- Rust's type system requires explicit handling of `Option` and `Result` types, making nested pattern matching common
12+
- The feature addresses the need for more readable control flow when dealing with multiple optional values or validation chains
13+
- Let-chains were stabilized in Rust 1.64 after being proposed in RFC 2497
14+
- ## Key Principles
15+
- **Special expression syntax**: `let PATTERN = EXPR` becomes a boolean-producing expression only inside `if` / `while` conditions, not a normal statement
16+
- **Pattern matching with binding**: Each `let` clause performs pattern matching and introduces bindings that propagate forward through the condition chain and into the body
17+
- **Short-circuiting**: Like normal boolean expressions, let-chains short-circuit—if any pattern fails to match, evaluation stops and the condition becomes `false`
18+
- **Forward propagation**: Bindings from earlier `let` clauses are available in subsequent clauses and in the body
19+
- **Expression-oriented**: Let-chains produce boolean values, enabling them to be combined with `&&` and `||` operators
20+
- ## Mechanism
21+
- In the context of `if` or `while` conditions, `let PATTERN = EXPR` evaluates the expression and attempts to match it against the pattern
22+
- If the pattern matches:
23+
- Variables are bound to matched components
24+
- The expression evaluates to `true`
25+
- Evaluation continues to the next clause
26+
- If the pattern doesn't match:
27+
- The expression evaluates to `false`
28+
- Short-circuiting occurs (for `&&` chains)
29+
- The condition fails and the body is not executed
30+
- The compiler desugars let-chains into nested `match` expressions, but the linear syntax is much more readable
31+
- Example desugaring:
32+
- ~~~rust
33+
if let Some(x) = a() && let Some(y) = b(x) && y > 10 {
34+
// body
35+
}
36+
~~~
37+
- Is conceptually equivalent to:
38+
collapsed:: true
39+
- ~~~rust
40+
match a() {
41+
Some(x) => {
42+
match b(x) {
43+
Some(y) if y > 10 => {
44+
// body
45+
}
46+
_ => {}
47+
}
48+
}
49+
_ => {}
50+
}
51+
~~~
52+
- ## Examples
53+
- ### Basic Let-Chain
54+
- ~~~rust
55+
if let Some(source) = deployment["source"].as_str()
56+
&& source == "github"
57+
&& let Some(source_config) = deployment["source_config"].as_object()
58+
&& let Some(integration_id) =
59+
source_config.get("integration_id").and_then(|v| v.as_str())
60+
{
61+
// All conditions matched, use source, source_config, and integration_id
62+
}
63+
~~~
64+
- ### Without Let-Chains (Nested Approach)
65+
- ~~~rust
66+
if let Some(source) = deployment["source"].as_str() {
67+
if source == "github" {
68+
if let Some(source_config) = deployment["source_config"].as_object() {
69+
if let Some(integration_id) =
70+
source_config.get("integration_id").and_then(|v| v.as_str())
71+
{
72+
// All conditions matched
73+
}
74+
}
75+
}
76+
}
77+
~~~
78+
- ### Chaining Multiple Patterns
79+
- ~~~rust
80+
if let Some(x) = a()
81+
&& let Some(y) = b(x)
82+
&& y > 10
83+
{
84+
// Use x and y here
85+
}
86+
~~~
87+
- ### Comparison to Python's Walrus Operator
88+
- Python's walrus operator (`:=`) provides similar functionality but is binding-only
89+
- Rust's let-chains provide binding + structural matching + destructuring
90+
- Python example:
91+
collapsed:: true
92+
- ~~~python
93+
if (x := f()) is not None and (y := g(x)) > 10:
94+
# use x and y
95+
~~~
96+
- Rust equivalent:
97+
collapsed:: true
98+
- ~~~rust
99+
if let Some(x) = f()
100+
&& let Some(y) = g(x)
101+
&& y > 10
102+
{
103+
// use x and y
104+
}
105+
~~~
106+
- ## Where Let-Chains Work
107+
- ✅ `if let` conditions
108+
- ✅ `while let` conditions
109+
- ✅ `match` guards (pattern guards)
110+
- ❌ Not allowed in general expression contexts (e.g., `let x = let ...`)
111+
- ❌ Not allowed in arbitrary boolean expressions outside `if`/`while` (e.g., `foo && let x = bar`)
112+
- ❌ Not allowed with parentheses (must use `if let`, not `if (let x = y)`)
113+
- ## Misconceptions
114+
- Let-chains are just pattern matching → **False**. They're pattern-matching expressions embedded in boolean expression chains, combining multiple concepts
115+
- `let` in conditions is the same as normal `let` → **False**. It's special expression syntax that only works in `if`/`while` conditions
116+
- Let-chains are the same as Python's walrus operator → **False**. Walrus is binding-only; let-chains provide structural matching and destructuring
117+
- You can use let-chains anywhere → **False**. They only work in `if` and `while` condition expressions
118+
- Let-chains always improve readability → **False**. They can hurt readability when bindings are logically independent or when error context is needed
119+
- ## When to Use Let-Chains
120+
- **Great for**:
121+
- Nested `Option` / `Result` handling
122+
- Validation funnels where each step depends on the previous
123+
- Protocol decoding with multiple optional fields
124+
- Deeply nested struct access patterns
125+
- Early bail conditions with binding
126+
- Replacing nested `if let` statements that form a "pyramid of doom"
127+
- **Avoid when**:
128+
- Readability suffers from too many chained conditions
129+
- Bindings are logically independent (consider separate `if let` statements)
130+
- You need error context (use `match` or `let else` instead)
131+
- The chain becomes too long or complex
132+
- ## Mental Model
133+
- Think of `&&` in let-chains as: "Proceed only if this expression is true — and bind variables if applicable"
134+
- Each `let` clause:
135+
- Performs pattern matching
136+
- Stops evaluation if it fails (short-circuiting)
137+
- Introduces new bindings into the rest of the chain and body
138+
- The whole condition short-circuits like a normal boolean expression chain
139+
- If walrus is "Assign while testing", then let-chains are **"Match while testing"**
140+
- ## Formal Terminology
141+
- ✅ **Let-chains** (official name)
142+
- ✅ **`if let` chains**
143+
- ✅ **Pattern guards** (sometimes used colloquially)
144+
- ✅ **Conditional pattern bindings**
145+
- ❌ Not called walrus
146+
- ❌ Not assignment-expression
147+
- ❌ Not general pattern matching
148+
- ## Related
149+
- [[Programming/Concept/Pattern Matching]] - General pattern matching concept
150+
- [[Rust/match]] - Rust's `match` expression for pattern matching
151+
- [[Rust/Option]] - The `Option` type commonly used with let-chains
152+
- [[Rust/Result]] - The `Result` type commonly used with let-chains
153+
- [[Programming/Destructuring]] - Extracting values from structures

0 commit comments

Comments
 (0)