Software Development

Unnamed Patterns and Variables: Reducing Boilerplate

There’s a quiet revolution happening in modern programming languages, and it’s all about what you don’t write. Unnamed patterns and variables—often called discards or wildcards—are reshaping how we handle data we don’t care about, making code cleaner, more intentional, and remarkably more readable.

The Problem with Naming Everything

We’ve all been there. You’re destructuring a tuple or matching a pattern, and you need to name variables you’ll never use. So you write unused, _temp, dummy, or the classic programmer’s fallback: x. These ghost variables clutter your code and create cognitive noise. Worse, they suggest these values might matter when they don’t.

Consider parsing a CSV line where you only care about the third field:

// Traditional approach - naming what we don't need
var parts = line.Split(',');
var field1 = parts[0];  // Don't care
var field2 = parts[1];  // Don't care
var field3 = parts[2];  // This is what we want
var field4 = parts[3];  // Don't care

Every unnecessary name is a small tax on the reader’s attention. They have to process each variable, wonder if it’s used later, and mentally filter the noise from the signal.

Enter the Discard: The Underscore Revolution

The discard pattern, represented by _, is a formal way to say “I acknowledge this value exists, but I don’t care about it.” It’s not a variable—it’s the absence of one. You can’t read from it, can’t reference it later, and that’s precisely the point.

Languages like C#, Python, Rust, and Go have embraced discards, though with varying syntax and capabilities. Here’s how the CSV example transforms:

// With discards - crystal clear intent
var (_, _, field3, _) = (parts[0], parts[1], parts[2], parts[3]);
// Or better yet with deconstruction
var (_, _, value, _) = ParseCsvLine(line);

The underscore screams “this doesn’t matter.” There’s no ambiguity, no wondering if temp2 gets used somewhere down the function. The intent is explicit.

Pattern Matching Without the Ceremony

Unnamed patterns truly shine in pattern matching scenarios. When you’re matching against complex types but only care about specific properties, discards eliminate the boilerplate of naming things you’ll immediately ignore.

Take a typical example from event handling:

// Old way - verbose naming
switch (message)
{
    case LoginEvent(var userId, var timestamp, var ipAddress):
        LogAccess(userId, timestamp, ipAddress);
        break;
    case LogoutEvent(var userId, var timestamp, var sessionDuration):
        LogSession(userId, sessionDuration);
        break;
}

But what if LogSession doesn’t need the timestamp? You’re forced to name it anyway. With discards:

// With discards - only name what matters
switch (message)
{
    case LoginEvent(var userId, var timestamp, var ip):
        LogAccess(userId, timestamp, ip);
        break;
    case LogoutEvent(var userId, _, var duration):
        LogSession(userId, duration);
        break;
}

The code now documents its own requirements. At a glance, you know which properties each handler actually uses.

Multiple Discards: Freedom from Uniqueness

One of the most liberating aspects of discards is that you can use _ multiple times in the same pattern. Unlike regular variables, which must have unique names, discards are anonymous by nature.

// Beautiful - no need for _, _2, _3, etc.
if (tuple is (_, _, var important, _, _, _))
{
    Process(important);
}

This seems minor until you’re destructuring seven-element tuples returned from legacy APIs. The alternative—var unused1, var unused2, var unused3—is soul-crushing boilerplate.

Out Parameters: A Common Pain Point

C# developers know the pain of out parameters, especially when calling methods that return multiple values through this mechanism. Before discards, you had to declare variables even if you’d never use them:

// Old way - declaring unused variables
int result;
if (int.TryParse(input, out result))
{
    // Use result
}

// But what about this?
string output;
bool success = TryDoSomething(input, out output, out int errorCode);
// Don't care about errorCode but had to declare it

With discard out parameters, you cut straight to what matters:

// Clean - discard what you don't need
if (int.TryParse(input, out var result))
{
    // Use result
}

bool success = TryDoSomething(input, out var output, out _);
// errorCode discarded, intent clear

Lambda Parameters: The Silent Arguments

Lambda expressions often receive parameters they don’t use. Event handlers are notorious for this—you need to match a delegate signature, but you rarely care about the sender or event args in simple scenarios.

// Traditional - naming unused parameters
button.Click += (sender, args) => 
{
    // Don't use sender or args
    RefreshData();
};

Modern C# lets you discard these entirely:

// With discards - obvious that parameters don't matter
button.Click += (_, _) => RefreshData();

Some languages, like Rust, take this further by not even requiring you to name unused closure parameters, though explicit discards remain clearer for readers.

Deconstruction: Selective Extraction

Deconstruction is where unnamed patterns really prove their worth. When working with records, tuples, or any deconstructable type, you can cherry-pick exactly what you need.

public record Person(string FirstName, string LastName, int Age, string Email);

// Extract only what you need
var (firstName, _, _, email) = GetPerson();
SendEmail(email, $"Hello {firstName}");

This is particularly powerful with positional records, where properties are accessed by position rather than name. The discard pattern makes positional deconstruction practical by letting you skip irrelevant fields without counting positions.

List Patterns and Slice Discards

C# 11 introduced list patterns, and with them came the slice discard pattern .., which matches zero or more elements you don’t care about. This is perfect for matching against collections where you only care about specific positions.

int[] scores = GetTestScores();

string Evaluate(int[] scores) => scores switch
{
    [100, ..] => "Perfect start!",
    [.., 100] => "Strong finish!",
    [var first, .., var last] => $"Started {first}, ended {last}",
    _ => "No pattern matched"
};

The .. pattern says “I don’t care how many elements are here or what they are.” It’s the collection equivalent of the single discard, and it’s just as liberating.

The Psychology of Naming

There’s something deeper happening here than just shorter code. Naming is hard—famously one of the two hard problems in computer science (along with cache invalidation and off-by-one errors). Every name you create carries cognitive weight.

When you name something, you imply it matters. You suggest there’s a concept worth referring to. Discards flip this around: by not naming, you communicate that this value is incidental to your logic. The code becomes self-documenting in what it ignores as much as what it uses.

This aligns with the principle of least surprise. When a maintainer sees var userId, var _, var sessionId, they immediately know the middle value is irrelevant to the logic that follows. There’s no need to scan the function looking for where temp or unused2 might be referenced.

Performance and Compiler Optimizations

Discards aren’t just syntactic sugar—they give the compiler optimization opportunities. When you discard a value, the compiler knows it doesn’t need to store it in a register or stack slot. For large objects, the runtime might be able to skip expensive operations entirely.

In pattern matching, discards can help the compiler generate more efficient dispatch code. When you specify exactly which fields you’re checking versus which you’re ignoring, the compiler can potentially skip unnecessary comparisons or property accesses.

The performance gains are usually modest, but they’re a nice bonus on top of the readability benefits.

When Not to Use Discards

Like any feature, discards can be overused. If you’re discarding most of a complex structure, that might signal your API is poorly designed. Maybe you should return a more focused type, or provide multiple methods that return different subsets of data.

// Code smell - why return so much if we need so little?
var (important, _, _, _, _, _, _) = GetSevenThings();

This might be a sign that GetSevenThings() should be GetImportantThing() or that you need separate methods for different use cases.

Similarly, in tuple-heavy code, excessive discarding might indicate you should use a proper class or record with named properties instead of relying on positional deconstruction.

Cross-Language Patterns

The discard concept appears across many languages, though syntax varies. Python uses _ as a convention (though it’s technically a valid variable name). Rust has _ for ignoring values and .. for ignoring remaining struct fields. Go uses _ for ignoring return values and import side effects.

# Python - convention-based discards
first, _, third = get_tuple()
for _ in range(10):  # Classic "I just want to loop N times"
    do_something()
// Rust - compile-time enforced ignoring
let (x, _, z) = get_tuple();
match point {
    Point { x, .. } => println!("x: {}", x),  // Ignore other fields
}

This cross-language consistency makes discards part of the shared vocabulary of modern programming, making code more immediately understandable to developers coming from different language backgrounds.

The Future: More Expressive Ignoring

Languages continue to evolve their discard capabilities. Proposals exist for named discards (to document what you’re ignoring without actually binding it), conditional discards in more contexts, and better integration with type inference.

The trend is clear: modern languages want to help you write code that focuses on what matters while elegantly handling what doesn’t. Every unnecessary name removed is a step toward clearer, more maintainable code.


Useful Links

Language Documentation:

Proposals and Evolution:

Best Practices:

Community Discussions:

Eleftheria Drosopoulou

Eleftheria is an Experienced Business Analyst with a robust background in the computer software industry. Proficient in Computer Software Training, Digital Marketing, HTML Scripting, and Microsoft Office, they bring a wealth of technical skills to the table. Additionally, she has a love for writing articles on various tech subjects, showcasing a talent for translating complex concepts into accessible content.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Back to top button