Skip to content
DeveloperMemos

Checking out Swift's @discardableResult

Swift, Functions, Annotations2 min read

Swift offers a variety of annotations that allow developers to add extra information to their code. One such annotation is @discardableResult, which can be used to inform the compiler that the result of a function should be discarded. In this article, we'll take a closer look at @discardableResult and examine some examples of when it might be useful.

The Basics

When a function returns a value, Swift requires that you do something with that value. You might assign it to a variable, use it in an expression, or pass it as a parameter to another function. Sometimes, however, you might not care about the return value of a function. For example, consider a function that logs a message to the console:

1func log(_ message: String) {
2 print("(message)")
3}

This function doesn't return anything, so you don't need to do anything with its result. However, if you try to call this function without using its result, the Swift compiler will generate a warning:

1log("Hello, world!") // Warning: Result of call to 'log' is unused

This warning is helpful because it reminds you that you're not doing anything with the output of the log function. However, sometimes you might want to intentionally discard the result of a function. That's where @discardableResult comes in.

To mark a function's return value as discardable, you simply add the @discardableResult annotation before the function definition:

1@discardableResult
2func log(_ message: String) -> Bool {
3 print("(message)")
4 return true
5}

Now, if you call this function without using its result, you won't get a warning:

1log("Hello, world!")

When to Use @discardableResult

So why would you want to use @discardableResult? There are a few situations where it can be helpful:

1. Convenience Functions

Sometimes you might write a function that returns a value, but you know that callers won't always care about that value. In that case, you might choose to mark the function's return value as discardable. This allows callers to omit the return value and avoid the "unused result" warning.

For example, consider a function that generates a random integer in a given range:

1@discardableResult
2func randomInt(in range: Range<Int>) -> Int {
3 let lower = range.lowerBound
4 let upper = range.upperBound - 1
5 return Int.random(in: lower...upper)
6}

If callers don't need the exact value of the random integer, they can simply call the function without assigning its result to a variable:

1randomInt(in: 1..<10)

2. Optional Chaining

Another situation where @discardableResult can be useful is when you're using optional chaining. When you use optional chaining to call a method on an optional object, the result is also optional. If you don't care about the result of the method, you can use @discardableResult to suppress any warnings about the unused optional value.

For example, consider a class that has a method for logging a message:

1class Logger {
2 func log(_ message: String) -> Bool {
3 print("(message)")
4 return true
5 }
6}

If you have an instance of this class and want to call its log method using optional chaining, you can use @discardableResult to avoid any warnings about the unused optional value:

1let logger: Logger? = Logger()
2logger?.log("Hello, world!")

3. Fluent Interfaces

Finally, @discardableResult can be useful when you're building fluent interfaces. A fluent interface is one where you chain method calls together in order to create a "fluent" sequence of operations.

For example, consider a hypothetical library that allows you to perform various image processing operations. You might write a Processor class with methods for each operation:

1class Processor {
2 func crop(to rect: CGRect) -> Processor {
3 // ...
4 return self
5 }
6
7 func resize(to size: CGSize) -> Processor {
8 // ...return self
9 }
10
11 func rotate(by angle: CGFloat) -> Processor {
12 // ...
13 return self
14 }
15
16 // Other methods...
17}

With this class, you can chain together method calls to create a sequence of image processing operations:

1let processor = Processor()
2 .crop(to: CGRect(x: 0, y: 0, width: 100, height: 100))
3 .resize(to: CGSize(width: 200, height: 200))
4 .rotate(by: .pi / 4)

In this example, each method returns an instance of Processor, which allows you to chain the method calls together. However, callers might not care about the result of each method call. In that case, you can mark the return value of each method as discardable:

1class Processor {
2 @discardableResult func crop(to rect: CGRect) -> Processor {
3 // ...
4 return self
5 }
6
7 @discardableResult func resize(to size: CGSize) -> Processor {
8 // ...
9 return self
10 }
11
12 @discardableResult func rotate(by angle: CGFloat) -> Processor {
13 // ...
14 return self
15 }
16
17 // Other methods...
18}

Now, callers can use the fluent interface without worrying about warnings for unused results:

1let processor = Processor()
2 .crop(to: CGRect(x: 0, y: 0, width: 100, height: 100))
3 .resize(to: CGSize(width: 200, height: 200))
4 .rotate(by: .pi / 4)

Conclusion

@discardableResult is a useful annotation in Swift that allows developers to indicate that they don't care about the return value of a function. By using @discardableResult, you can avoid warnings for unused results and make your code more readable.