Have you ever stared at Swift code and wondered about the mysterious underscore (_) character popping up in seemingly random places? Perhaps you’ve seen it dismissing function parameters, ignoring return values, or even as a placeholder in loops. Understanding the purpose of underscores in Swift is crucial for writing cleaner, more efficient, and more readable code. Ignoring them can lead to confusion and potentially hinder your ability to fully grasp the language’s capabilities. So, why do I need underscores in Swift? This article will demystify the various uses of underscores, providing clear explanations, real-world examples, and practical tips to help you master this essential Swift feature. We’ll explore how they contribute to code clarity, prevent naming conflicts, and enhance code maintainability. Think of it as your comprehensive guide to understanding the power of the Swift underscore.
Ignoring Values with Underscores
One of the most common uses of the underscore in Swift is to ignore values. When a function returns multiple values (as a tuple), or when iterating through a collection, you might not need all the returned or iterated values. Using an underscore allows you to explicitly signal to the compiler (and to anyone reading your code) that you are intentionally discarding a particular value. This avoids compiler warnings about unused variables and makes your intentions crystal clear. This practice keeps your code clean and focused on the essential operations.
For example, consider a function that returns both a result and an error code. If you are only interested in the result and are confident that the operation will succeed, you can ignore the error code using the underscore. Similarly, when iterating through a dictionary, you might only need the values and not the keys. In this case, you can use an underscore to ignore the keys. This is particularly useful when dealing with API responses or data structures where some fields might be irrelevant to your current task. According to Apple’s documentation [1], using the underscore clarifies the intention and avoids unnecessary variable declarations.
Let’s look at a practical example. Imagine you’re using a method that returns both a file path and a boolean indicating success. If you only care about whether the operation succeeded, you can write: let (_, success) = fileOperation(). The underscore effectively tells Swift to disregard the file path. This not only cleans up your code but also enhances its readability.
External Parameter Names and the Underscore
In Swift, function parameters can have both internal and external names. Internal names are used within the function’s implementation, while external names are used when calling the function. Sometimes, you might want to omit the external parameter name for a more natural function call syntax. This is where the underscore comes in handy. By using an underscore as the external parameter name, you effectively tell Swift not to require an external name when calling the function.
Consider a function designed to add two numbers. Without the underscore, you might have to call it like this: add(number1: 5, number2: 3). Using underscores, you can define the function as func add(_ number1: Int, _ number2: Int) -> Int, and call it simply as add(5, 3), which is often more readable. This is particularly useful for functions that perform common operations where the parameter names are obvious from the context. According to the Swift style guide [2], prioritizing clarity at the point of use is a key principle of good API design.
The use of underscores for external parameter names is a stylistic choice, and it’s important to use it judiciously. Overusing it can make your code less readable if the purpose of the parameters is not immediately clear. However, in certain cases, it can significantly improve the flow and readability of your code. For example, in UIKit, many methods use underscores for parameters representing coordinates or sizes, leading to a more natural and concise syntax. To learn more about Swift syntax, you can consult online resources such as Swift Playgrounds.
Ignoring Loop Variables
When iterating through a range or collection using a for loop, you might not always need the loop variable itself. For example, you might want to execute a block of code a certain number of times without needing to know the current iteration count. In such cases, you can use an underscore as the loop variable to indicate that you are intentionally ignoring it. This is a common practice that improves code readability and avoids unnecessary variable declarations.
Using an underscore in a for loop tells the compiler that you don’t need to access the current iteration number. Consider this example: for _ in 0..<5 { print(“Hello”) }. This code will print “Hello” five times, but the loop variable (which would normally hold the current iteration number) is ignored. This is a clear and concise way to express the intent to simply repeat a block of code a certain number of times. This technique is particularly useful when dealing with animations or other repetitive tasks where the iteration count is irrelevant.
This practice is not just about avoiding compiler warnings; it’s about making your code more expressive and easier to understand. When someone reads your code and sees for _ in …, they immediately know that the loop variable is not being used, which helps them focus on the relevant parts of the code. Itโs important to remember that readability is a crucial aspect of maintainable code, so using underscores effectively contributes to long-term code quality. Hereโs a summary of benefits:
- Improved Code Readability
- Avoidance of Compiler Warnings
- Clear Indication of Intent
Pattern Matching and the Wildcard Pattern
The underscore also plays a crucial role in pattern matching within switch statements and if case statements. In this context, the underscore acts as a wildcard pattern, matching any value. This is particularly useful when you want to handle specific cases but also provide a default case that catches all other possibilities. The wildcard pattern ensures that your code handles all possible inputs without requiring you to explicitly list every single case.
Consider a switch statement that handles different types of errors. You might want to handle specific error types with custom logic, but also provide a generic error handler for all other error types. The wildcard pattern allows you to do this easily. For example: switch error { case .networkError: // Handle network error case .fileNotFoundError: // Handle file not found error case _: // Handle all other errors }. The underscore in the last case acts as a catch-all, ensuring that any error that doesn’t match the previous cases is handled gracefully.
Here is a featured snippet-optimized paragraph: The underscore as a wildcard pattern is essential for robust error handling and flexible data processing. It allows you to create comprehensive switch statements that cover all possible scenarios, while also providing a default case for unexpected inputs. This ensures that your code is resilient and can handle a wide range of situations. By using the wildcard pattern, you can avoid writing repetitive code and make your switch statements more concise and readable. This is a powerful tool for writing clean, maintainable, and error-resistant code. Learn more about Swift’s pattern matching capabilities.
While the underscore is a powerful tool, it’s important to use it judiciously. Overusing underscores can make your code less readable if the intent is not immediately clear. It’s crucial to strike a balance between conciseness and clarity. Always consider whether using an underscore improves the readability of your code or makes it more confusing. For example, if you are ignoring a value that is essential to understanding the logic of your code, it might be better to assign it to a descriptive variable name, even if you don’t use it directly. The Swift community emphasizes readability, as detailed on Stack Overflow [3], in many discussions.
Here are some general guidelines to follow when using underscores in Swift:
- Use underscores to ignore values that are not relevant to the current task.
- Use underscores as external parameter names only when it improves the readability of the function call.
- Use underscores as loop variables when you don’t need the current iteration count.
- Use underscores as wildcard patterns in switch statements to handle default cases.
Adhering to these best practices will help you write cleaner, more maintainable, and more readable Swift code. Always prioritize clarity and make sure that your code clearly communicates its intent to other developers (and to your future self!). A good rule of thumb is to always ask yourself: “Will someone else be able to understand this code easily?”. If the answer is no, then you might need to rethink your use of underscores.
- Prioritize Code Clarity.
- Maintain Consistency in Usage.
FAQ
- Q: When should I use an underscore to ignore a variable?
- A: Use an underscore when you are intentionally discarding a value that is returned by a function or when iterating through a collection, and you don't need to use that value in your code.
- Q: Is it always better to use an underscore for external parameter names?
- A: No, it's not always better. Use an underscore for external parameter names only when it improves the readability of the function call and the purpose of the parameters is clear from the context.
- Q: Can I use multiple underscores in a single statement?
- A: Yes, you can use multiple underscores in a single statement to ignore multiple values. For example: let (\_, \_, result) = someFunctionThatReturnsATuple().
The only way I can get these functions to print was by using the underscores before the parameters:
func divmod(_ a: Int, _ b:Int) -> (Int, Int) { return (a / b, a % b) } print(divmod(7, 3)) print(divmod(5, 2)) print(divmod(12,4))
Without the underscores I have to write it like this to avoid any errors:
func divmod(a: Int, b:Int) -> (Int, Int) { return (a / b, a % b) } print(divmod(a: 7, b: 3)) print(divmod(a: 5, b: 2)) print(divmod(a: 12,b: 4))
I don’t understand this underscore usage. When, how and why do I use these underscores?
There are a few nuances to different use cases, but generally an underscore means “ignore this”.
When declaring a new function, an underscore tells Swift that the parameter should have no label when called โ that’s the case you’re seeing. A fuller function declaration looks like this:
func myFunc(label name: Int) // call it like myFunc(label: 3)
“label” is an argument label, and must be present when you call the function. (And since Swift 3, labels are required for all arguments by default.) “name” is the variable name for that argument that you use inside the function. A shorter form looks like this:
func myFunc(name: Int) // call it like myFunc(name: 3)
This is a shortcut that lets you use the same word for both external argument label and internal parameter name. It’s equivalent to func myFunc(name name: Int).
If you want your function to be callable without parameter labels, you use the underscore _ to make the label be nothing/ignored. (In that case you have to provide an internal name if you want to be able to use the parameter.)
func myFunc(_ name: Int) // call it like myFunc(3)
In an assignment statement, an underscore means “don’t assign to anything”. You can use this if you want to call a function that returns a result but don’t care about the returned value.
_ = someFunction()
Or, like in the article you linked to, to ignore one element of a returned tuple:
let (x, _) = someFunctionThatReturnsXandY()
When you write a closure that implements some defined function type, you can use the underscore to ignore certain parameters.
PHPhotoLibrary.performChanges( { /* some changes */ }, completionHandler: { success, _ in // don't care about error if success { print("yay") } })
Similarly, when declaring a function that adopts a protocol or overrides a superclass method, you can use _ for parameter names to ignore parameters. Since the protocol/superclass might also define that the parameter has no label, you can even end up with two underscores in a row.
class MyView: NSView { override func mouseDown(with _: NSEvent) { // don't care about event, do same thing for every mouse down } override func draw(_ _: NSRect) { // don't care about dirty rect, always redraw the whole view } }
Somewhat related to the last two styles: when using a flow control construct that binds a local variable/constant, you can use _ to ignore it. For example, if you want to iterate a sequence without needing access to its members:
for _ in 1...20 { // or 0..<20 // do something 20 times }
If you’re binding tuple cases in a switch statement, the underscore can work as a wildcard, as in this example (shortened from one in The Swift Programming Language):
switch somePoint { // somePoint is an (Int, Int) tuple case (0, 0): print("(0, 0) is at the origin") case (_, 0): print("(\(somePoint.0), 0) is on the x-axis") case (0, _): print("(0, \(somePoint.1)) is on the y-axis") default: print("(\(somePoint.0), \(somePoint.1)) isn't on an axis") }
One last thing that’s not quite related, but which I’ll include since (as noted by comments) it seems to lead people here: An underscore in an identifier โ e.g. var _foo, func do_the_thing(), struct Stuff_ โ means nothing in particular to Swift, but has a few uses among programmers.
Underscores within a name are a style choice, but not preferred in the Swift community, which has strong conventions about using UpperCamelCase for types and lowerCamelCase for all other symbols.
Prefixing or suffixing a symbol name with underscore is a style convention, historically used to distinguish private/internal-use-only symbols from exported API. However, Swift has access modifiers for that, so this convention generally is seen as non-idiomatic in Swift.
A few symbols with double-underscore prefixes (func __foo()) lurk in the depths of Apple’s SDKs: These are (Obj)C symbols imported into Swift using the NS_REFINED_FOR_SWIFT attribute. Apple uses that when they want to make a “more Swifty” version of an (Obj)C API โ for example, to make a type-agnostic method into a generic method. They need to use the imported API to make the refined Swift version work, so they use the __ to keep it available while hiding it from most tools and documentation.