Swift “_” Operator – When Use Underscore in Swift?

In Swift, the underscore operator (_) represents an unnamed parameter/label.

In for loops, using the underscore looping parameter is practical if you do not need the looping parameter in the loop.

for _ in 1...5 {
    print("Hello")
}

Output:

Hello
Hello
Hello
Hello
Hello

In function calls, you can omit the argument label by adding a (_) in front of the argument in the implementation.

func greet(_ name: String) {
    print("Hello, \(name)")
}

greet("Alice")

Output:

Hello, Alice

In this guide, you learn eight different ways on how to use the underscore operator (_) in Swift.

Underscore (_) Usecases in Swift

Here are different situations where you can use the underscore operator:

1. Omit Labels from Function Arguments

In Swift, you need to specify the function argument labels by default when calling the function. Sometimes this can be frustrating because the label makes the syntax look verbose or repetitive.

showNameOfPerson(nameOfPerson: "Jack")

You can skip having to define the label for a function argument by using the underscore operator (_) when defining the function.

For example:

func greet(_ name: String) {
    print("Hello, \(name)")
}

greet("Alice")

Output:

Hello, Alice

2. Skip Looping Parameters

As you know, you can give a name to the looping parameter in a forin loop in Swift. However, if you are not using the looping parameter inside the loop, you can skip giving a name to the parameter. This happens by using underscore (_).

For example:

for _ in 1...5 {
    print ("Hello")
}

Output:

Hello
Hello
Hello
Hello
Hello

3. Ignore Tuple Constituents

In Swift, you can use tuple destructuring to pull apart values from a tuple in a single line of code.

However, if you only care about some particular components of the tuple, you can skip the ones you do not like with the underscore operator (_).

For example, let’s only extract x, y, z coordinates from a 4D coordinate point:

let coords = (1.0, 2.0, 0.5, 2.5)

let (x, y, z, _) = coords

print(x, y, z)

Output:

1.0 2.0 0.5

Let’s see another example of discarding a tuple component in a switch-case statement.

For example, let’s rate a (name, score) data in a golf scorecard. The score does not depend on the name of the player. Thus we can discard the name:

let score = ("Alice", 3)

switch score {
case (_, 3):
    print("Par")
case (_, 2):
    print("Birdie")
case (_, 1):
    print("Ace")
default:
    fatalError("Unsupported")
}

Output:

Par

4. Make Numbers Readable

If you have a big or small number with a lot of digits, it can help to separate thousands with underscores. This makes reading the numbers easier.

Here are some examples:

1_000_000      // instead of 1000000
0.000_000_001  // instead of 0.000000001

1_403_182_938  // instead of 1403182938
0.619_241_053  // instead of 0.619241053

5. Ignore Associated Values in Enumerations

You can associate enumeration cases with values by specifying the types of the values as arguments in the enumeration cases.

If an enumeration case is associated with a value you do not care about, you can skip it using the underscore (_) operator.

For example, let’s create an Action enumeration that describes actions that happen in x and y-directions:

enum Action {
    case Driving(Int, Int)
    case Walking(Int, Int)
}

Let’s then write a function into the enumeration that shows the action only in the x-direction. As we do not care about the y-direction, we can skip the associated value related to the action in y-direction:

enum Action {
    case Driving(Int, Int)
    case Walking(Int, Int)

    func horizontalMovement() {
        switch self {
        case .Driving(let xSpeed, _):
            print("Driving \(xSpeed)mph in x-direction")
        case .Walking(let xSpeed, _):
            print("Walking \(xSpeed)mph in x-direction")
        default:
            fatalError("Unsupported")
        }
    }
}

Example call:

let a = Action.Driving(10, 5)
a.horizontalMovement()

Output:

Driving 10mph in x-direction

6. Skip Parameters in Closures

Similar to functions, closures can take arguments.

If you pass a closure into a higher-order function that operates on a collection, you need to specify the closure arguments such that they match with the collection elements.

If a collection consists of tuples, and you only care about one tuple component, you can discard the rest of the tuple components by using underscore in the closure arguments.

For example, given an array of (name, score) pairs, let’s print the names of those who scored less than 3:

let scores = [
    ("Alice",    3),
    ("Bob",      3),
    ("Charlie",  2),
    ("David",    3),
    ("Emmanuel", 2)
]

let lessThanThree = scores.filter { (_, score) in return score < 3 }

lessThanThree.forEach { (name, _) in print("\(name)") }

Output:

Charlie
Emmanuel

If you take a look at the first closure:

scores.filter { (_, score) in return score < 3 }

You can see that as we only care about the score, we do not need to specify the name in the closure arguments. However, we have to refer to it somehow to clarify the Swift compiler that the closure is working on tuples. This is why there is a double underscore.

And when it comes to the second closure, we only care about the name of the person, not the score. Thus we skip the score:

lessThanThree.forEach { (name, _) in print("\(name)") }

7. Skip Function Return Value

In Swift, you can store the return value of a function by declaring a variable/constant.

However, if you have a function whose return value you are not interested in, you can:

  • Call the function without storing the return value.
  • Call the function and store the return value into a nameless variable using the underscore (_) operator.

Example. Given a function that returns a value, such as a string:

func giveName() -> String {
    return "Alice"
}

You can discard the string returned by this function by:

giveName()

But you can also highlight that you are not interested in the value using the underscore (_) operator:

_ = giveName()

This action does not store the return value anywhere.

8. Skip Override Parameters Labeled with “_”

If you override a function that has an underscore, to begin with, you need to add another underscore to the overridden definition of the function.

For example, let’s create a Pet class and Cat class that inherits the Pet:

class Pet {
    let name: String
    init(name: String) {
        self.name = name
    }

    func timeTo(_ command: String) { 
        if command == "eat" { print("Yum!") }
        else if command == "sleep" { print("Zzz...") }
    }
}

class Cat: Pet {
    // Cat does not care about the command, let's skip it.
    override func timeTo(_ _: String) {
        print("Meow!")
    }
}

// Example calls:
let dog = Pet(name: "Snoopy")
dog.timeTo("sleep")

let cat = Cat(name: "Luna")
cat.timeTo("sleep")

Output:

Zzz...
Meow!

As you can see, there are two consecutive underscores in the overridden function:

The two underscores in a row mean that we omit the command argument from the subclass version of the function.

But why not remove the argument altogether?

If you removed the command argument, the code would fail. The Swift compiler would not recognize the function as something that comes from the parent class. Thus it has no way to override it.

// Fails
override func timeTo() {
    print("Meow!")
}

If you remove the override keyword, the function would be a completely separate function that has nothing to do with the function in the parent class.

// Works, but is inconsistent
func timeTo() {
    print("Meow!")
}

Conclusion

Today you learned how to use the underscore operator (_) in Swift.

To recap, you can use the underscore operator to skip the parameter/label name. This is sometimes useful in the following situations:

  • Omit Labels from Function Arguments
  • Skip Looping Parameters
  • Ignore Tuple Constituents
  • Make Numbers Readable
  • Ignore Associated Values in Enumerations
  • Skip Parameters in Closures
  • Skip Function Return Value
  • Skip Override Parameters

Thanks for reading.

Happy coding!

Further Reading

50 Swift Interview Questions