Hey Swifters, today we will explore the consume
operator in Swift 5.9. So, you are cruising through your Swift code, writing functions, structs, maybe even dabbling in generics. Then Swift 5.9 rolls in with something new, the consume
operator. And if your first reaction is:
“Wait… why do I need this? I’ve been doing just fine without it.”
You’re not alone. But let’s break it down clearly, and with code. 🧑💻
Swift is all about safety. Behind the scenes, it uses Automatic Reference Counting (ARC) to manage memory. When a value is no longer needed, Swift cleans it up. You usually don’t think twice about it. But sometimes you do care. Like when you are working in performance-critical environments (like games, machine learning, embedded systems), or writing lower-level Swift where memory overhead matters.
So What Is consume
, Anyway? 🤔
In Swift 5.9 the consume
operator was introduced to explicitly mark when a value is being consumed meaning:
“I’m done with this. Take it. Destroy it. Move on.”
This avoids extra retain/release calls, which means, better performance. It’s also part of Swift’s ongoing journey into ownership features (more details here), letting developers manage value lifetimes more precisely. You can read more about the proposal here (SE-0366 — consume
operator to end the lifetime of a variable binding). Let’s look at a real example
Before Swift 5.9 (Implicit Copying)
struct Ingredient {
var name: String
}
func use(_ ingredient: Ingredient) {
print("Using \(ingredient.name)")
}
var tomato = Ingredient(name: "Tomato")
use(tomato) // Copy happens here
use(tomato) // And again
Here, Swift copies tomato
each time you pass it to use()
. That’s fine for a tiny struct, but for large objects, it can be inefficient.
After Swift 5.9 (Enter consume
)
func use(_ ingredient: Ingredient) {
print("Used up \(ingredient.name)")
}
var tomato = Ingredient(name: "Tomato")
use(consume tomato) // or let tomatoCopy = consume tomato
use(tomato) // Ownership moved here; `tomato` cannot be used again
After consume tomato
, you’ve handed over the value. If you try to use tomato
after that, you’ll get a compile-time error. The compiler enforces this rule to make sure your code is safe and predictable. Pretty useful right? You can also define a parameter that expects a consumed value:
func build(_ thing: consuming Thing) {
// use thing here, it's moved
}
Now callers must pass a consumed value. It makes ownership rules clear at the API level, just like inout
clarifies mutation.
Alright, So When Should You Use consume
?
Well you don’t need it for every app, especially if performance isn’t a big concern. But here are good places to consider it:
- Game dev where real-time performance matters
- ML/data processing and working with big data blobs
- Embedded/IoT with low memory footprint
- System-level Swift or building your own frameworks or allocators
So that was a brief exploration of the consume
keyword i hope you liked it 🙂 Stay tuned for more interesting articles.