12

Does using let _ = ... have any purpose at all?

I've seen question and answers for What's the _ underscore representative of in Swift References? and I know that the underscore can be used to represent a variable that isn't needed.

This would make sense if I only needed one value of a tuple as in the example from the above link:

let (result, _) = someFunctionThatReturnsATuple()

However, I recently came across this code:

do {
    let _ = try DB.run( table.create(ifNotExists: true) {t in
        t.column(teamId, primaryKey: true)
        t.column(city)
        t.column(nickName)
        t.column(abbreviation)
        })

} catch _ {
    // Error throw if table already exists
}

I don't get any compiler warnings or errors if I just remove the let _ =. It seems to me like this is simpler and more readable.

try DB.run( table.create(ifNotExists: true) {t in
    t.column(teamId, primaryKey: true)
    t.column(city)
    t.column(nickName)
    t.column(abbreviation)
    })

The author of the code has written a book and a blog about Swift. I know that authors aren't infallible, but it made me wonder if there is something I am missing.

Community
  • 1
  • 1
Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
  • 1
    Here is an example where the assignment to `_` is needed to avoid a compiler warning about an unused result: http://stackoverflow.com/a/32788209/1187415. – Martin R Apr 19 '16 at 08:44
  • 1
    It short, `let _ = ` makes explicitly clear that the programmer wants to ignore the result of the method call. – Sulthan Apr 19 '16 at 08:48
  • The `let` seems to be optional when assigning to `_`. The blog author does it both ways. – vacawama Apr 19 '16 at 09:06
  • Be extra careful when explicitly disabling the `warn_unused_result ` with `let _ = ...`. There's usually a reason why this warning exist. – CouchDeveloper Apr 19 '16 at 09:38

7 Answers7

12

You will get a compiler warning if the method has been marked with a warn_unused_result from the developer documentation:

Apply this attribute to a method or function declaration to have the compiler emit a warning when the method or function is called without using its result.

You can use this attribute to provide a warning message about incorrect usage of a nonmutating method that has a mutating counterpart.

Community
  • 1
  • 1
sbarow
  • 2,759
  • 1
  • 22
  • 37
  • So using `let _ =` would basically trick the compiler into thinking that you were using the result and even if the method had been marked with `warn_unused_result` no warning would be given? – Suragch Apr 19 '16 at 08:48
  • Thats correct, but to the point made in the docs it might be an indication that you are using the method incorrectly. – sbarow Apr 19 '16 at 08:50
  • 13
    @Suragch Using `let _ =` is not tricking the compiler but rather letting it know that you know this value should be used but you chose not to use it. – Valentin Apr 19 '16 at 08:51
  • 1
    An example of this, and I'm not sure why this function is marked as `warn_unused_result`, is `UINavigationController`'s `popViewController(animated:)`. I almost never have a use for the return value. – Connor Neville Jan 13 '17 at 12:58
7

Using let _ = ... specifically tells the compiler that you know that the expression on the right returns a value but that you don't care about it.

In instances where the method has been marked with warn_unused_result, if you don't use the underscore then the compiler will complain with a warning. (Because in some cases it could be an error to not use the return value.)

Suragch
  • 484,302
  • 314
  • 1,365
  • 1,393
4

Sometimes it is simple and cleaner to use try? than do-catch, when you call something that throws, but decided not to handle any errors. If you leave call with try? as-is, compiler will warn you about unused result, which is not good. So you can discard results using _.

Example:

let _ = try? NSFileManager.defaultManager().moveItemAtURL(url1, toURL: url2)
Alexander Doloz
  • 4,078
  • 1
  • 20
  • 36
4

Also, let _ = or _ = can be used when right side of expression is lazy variable and you want it calculated right now, but have no use for the value of this variable yet.

A lazy stored property is a property whose initial value is not calculated until the first time it is used. You indicate a lazy stored property by writing the lazy modifier before its declaration.

Lazy properties are useful when the initial value for a property is dependent on outside factors whose values are not known until after an instance’s initialization is complete. Lazy properties are also useful when the initial value for a property requires complex or computationally expensive setup that should not be performed unless or until it is needed.


Example:

final class Example {
    private func deepThink() -> Int {
        // 7.5 million years of thinking
        return 42
    }

    private(set) lazy var answerToTheUltimateQuestionOfLifeTheUniverseAndEverything: Int = deepThink()

    func prepareTheAnswer() {
        _ = answerToTheUltimateQuestionOfLifeTheUniverseAndEverything
    }

    func findTheQuestion() -> (() -> Int) {
        // 10 millions of thinking
        let theQuestion = {
            // something
            return self.answerToTheUltimateQuestionOfLifeTheUniverseAndEverything
        }

        return theQuestion
    }
}

let example = Example()
// And you *want* to get the answer calculated first, but have no use for it until you get the question
example.prepareTheAnswer()

let question = example.findTheQuestion()
question()
user28434'mstep
  • 6,290
  • 2
  • 20
  • 35
3

It's also useful to print() something in SwiftUI.

struct ContentView: View {
    var body: some View {
        let _ = print("View was refreshed")
        Text("Hi")
    }
}

View was refreshed

You can use it to see the current value of properties:

struct ContentView: View {
    @State var currentValue = 0

    var body: some View {
        let _ = print("View was refreshed, currentValue is \(currentValue)")
        Button(action: {
            currentValue += 1
        }) {
            Text("Increment value")
        }
    }
}

View was refreshed, currentValue is 0
View was refreshed, currentValue is 1
View was refreshed, currentValue is 2
View was refreshed, currentValue is 3
View was refreshed, currentValue is 4

If you just did _ = print(...), it won't work:

struct ContentView: View {
    var body: some View {
        _ = print("View was refreshed") /// error!
        Text("Hi")
    }
}

Type '()' cannot conform to 'View'; only struct/enum/class types can conform to protocols

aheze
  • 24,434
  • 8
  • 68
  • 125
1

You can user also @discardableResult in your own functions if sometimes you don't need the result.

@discardableResult    
func someFunction() -> String {

}

someFunction() // Xcode will not complain in this case
0
if let _ = something {
    ...
}

is equal to

if something != nil {
    ...
}

If you replaced the underscore with a property name, the fix that Xcode would suggest is the second option (it would not suggest the first). And that's because the second option makes more programming sense. However—and this is something Apple themselves stress to developers—write the code that is the most readable. And I suspect the underscore option exists because in some cases it may read better than the other.

trndjc
  • 11,654
  • 3
  • 38
  • 51