Swift treats a returned value as something you probably meant to keep. If you call a function that produces a result and never use it, the compiler flags the call:
func addTrack(_ title: String) -> Int {
// ...
return 0
}
addTrack("Intro")
// ⚠️ Result of call to 'addTrack' is unusedMost of the time, that warning is a helpful nudge. But some APIs return a value as a convenience, such as an index, an identifier, or the modified instance itself. Many callers have no reason to look at it. For those functions, the warning becomes noise, and noise trains developers to ignore the Issue navigator.
The @discardableResult attribute lets the author of an API declare, once and in one place, that ignoring the return value is a normal way to call it.
Place the attribute directly before the function or method declaration:
@discardableResult
func addTrack(_ title: String) -> IntThat's the whole syntax. There are no arguments and no configuration.
Consider a small playlist type. Appending a track returns the new track's position, which is handy if you want to select or animate the new row, but pointless if you're just loading data.
struct Playlist {
private(set) var tracks: [String] = []
/// Adds a track to the end of the playlist.
///
/// - Parameter title: The title of the track to add.
/// - Returns: The index of the new track. The result can be ignored.
@discardableResult
mutating func append(_ title: String) -> Int {
tracks.append(title)
return tracks.count - 1
}
}
var playlist = Playlist()
playlist.append("Intro") // No warning.
let position = playlist.append("Outro") // Result put to use.Both call sites are valid, and the compiler stays quiet in each case.
The attribute is a natural fit for builder-style APIs that return Self so calls can be chained. Without it, every standalone configuration call would produce a warning:
final class RequestBuilder {
private var headers: [String: String] = [:]
@discardableResult
func header(_ name: String, value: String) -> Self {
headers[name] = value
return self
}
}
let builder = RequestBuilder()
// Standalone calls: no warnings.
builder.header("Accept", value: "application/json")
builder.header("X-Trace-ID", value: "42")
// Chained calls read just as well.
builder
.header("Accept", value: "application/json")
.header("X-Trace-ID", value: "42")@discardableResult composes with async and throws. It suppresses only the unused-result diagnostic. Any try or await your function requires remains mandatory.
struct UploadReceipt { let id: String }
@discardableResult
func upload(_ data: Data) async throws -> UploadReceipt {
// ...
return UploadReceipt(id: "abc123")
}
func save(_ data: Data) async throws {
try await upload(data) // No unused-result warning.
let receipt = try await upload(data) // Result available when needed.
print(receipt.id)
}Note Error handling stays visible at the call site. A discardable result never lets a failure slip by unnoticed, because throwing is expressed with
throws, not with the return value.
_ =Swift gives you two ways to say "I know there's a result, and I don't need it." They answer different questions.
@discardableResult |
_ = call() |
|
|---|---|---|
| Applied by | The API author | The caller |
| Written | Once, at the declaration | At every call site |
| Meaning | "Ignoring this result is normal." | "I'm ignoring this result on purpose, this time." |
| Best for | Convenience returns that most callers skip | Results that usually matter, but not right here |
Use the attribute when most callers won't need the value. Use _ = when the result matters in general and you're making a deliberate exception:
_ = try? await upload(data) // A conscious, visible decision.You've likely relied on this attribute without noticing. Several familiar APIs return a value that's useful on occasion and easy to skip the rest of the time:
Array.remove(at:) returns the element it removed.Set.insert(_:) reports whether the insertion happened and which member is in the set.Dictionary.updateValue(_:forKey:) returns the value that was replaced, if any.Each is annotated so that a bare call compiles cleanly. That's the same design bar to apply to your own code: the return value is a bonus, not a duty.
The attribute silences a diagnostic, so it's worth asking what that diagnostic was protecting. Skip @discardableResult when ignoring the value is likely a bug.
Pure functions. If a function's only purpose is to compute a value, dropping the value means the call did nothing. This is why sorted() and map(_:) deliberately keep the warning.
Results that carry failure. A return value that reports success or failure invites silent errors:
// ❌ A failed save disappears without a trace.
@discardableResult
func save() -> Bool {
// ...
}Prefer a throwing function, so failures can't be overlooked:
// ✅ Failure is explicit and enforced by the compiler.
func save() throws {
// ...
}A quick test: "If a caller ignores this value, could the program end up in a wrong or misleading state?" If the answer is yes, leave the warning in place.
Protocol requirements. The compiler evaluates the attribute on the declaration a call actually resolves to. If callers use your API through a protocol type or a generic constraint, put @discardableResult on the protocol requirement as well as on the conforming implementations.
protocol Queueing {
@discardableResult
mutating func enqueue(_ job: String) -> Int
}Functions returning Void. There's no result to discard, so the attribute has no effect. Adding it is harmless but redundant.
Function values. @discardableResult is a declaration attribute. A closure stored in a variable doesn't carry it, so an unused result from calling that closure still needs _ =.
Warnings as errors. If your project builds with warnings treated as errors, an unused result is a failed build, not just a yellow triangle. In that setup, deciding which functions are discardable becomes an API design decision rather than a cosmetic one.
Objective-C interop. Objective-C methods imported into Swift are generally treated as discardable unless they're explicitly annotated to warn on unused results. That's why some Cocoa calls never trigger the warning.
The attribute tells the compiler the result is optional. Tell your readers too. Use a Returns field in your documentation comment and state plainly that the value can be ignored:
/// Registers a handler for the given event.
///
/// - Parameter handler: The closure to call when the event fires.
/// - Returns: A token you can use to unregister the handler.
/// The token can be ignored if you never need to unregister.
@discardableResult
func observe(_ handler: @escaping () -> Void) -> ObservationToken {
// ...
}Xcode's Quick Help surfaces this text, so the contract is visible right where developers need it.
Swift 2 used an opt-in attribute, @warn_unused_result, to flag functions whose results mattered. Swift 3 inverted the model (Swift Evolution proposal SE-0047): every non-Void function now warns on an unused result by default, and @discardableResult is the explicit opt-out. Treating results as meaningful unless declared otherwise makes accidental omissions much easier to catch.
@discardableResult marks a function or method whose return value may be safely ignored.Self in fluent APIs.try or await requirements._ = when a result normally matters and you're skipping it only once.throws for errors.Returns field.Used deliberately, the attribute keeps your call sites quiet and your warnings meaningful, so a yellow triangle in Xcode is worth reading again.
Thank you for reading. If you have any questions feel free to follow me on X and send me a DM. If this article helped you, Buy me a coffee.