— Swift, Functions, Annotations — 2 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.
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@discardableResult2func log(_ message: String) -> Bool {3 print("(message)")4 return true5}
Now, if you call this function without using its result, you won't get a warning:
1log("Hello, world!")
So why would you want to use @discardableResult
? There are a few situations where it can be helpful:
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@discardableResult2func randomInt(in range: Range<Int>) -> Int {3 let lower = range.lowerBound4 let upper = range.upperBound - 15 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)
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 true5 }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!")
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 self5 }6
7 func resize(to size: CGSize) -> Processor {8 // ...return self9 }10
11 func rotate(by angle: CGFloat) -> Processor {12 // ...13 return self14 }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 self5 }6
7 @discardableResult func resize(to size: CGSize) -> Processor {8 // ...9 return self10 }11
12 @discardableResult func rotate(by angle: CGFloat) -> Processor {13 // ...14 return self15 }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)
@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.