Skip to main content
Version: 1.0.18

Handling Resources

This section looks at some of the common ways to safely handle resources using ZIO.

ZIO's resource management features work across synchronous, asynchronous, concurrent, and other effect types, and provide strong guarantees even in the presence of failure, interruption, or defects in the application.

Finalizing

ZIO provides similar functionality to try / finally with the ZIO#ensuring method.

Like try / finally, the ensuring operation guarantees that if an effect begins executing and then terminates (for whatever reason), then the finalizer will begin executing.

val finalizer = 
UIO.effectTotal(println("Finalizing!"))
// finalizer: UIO[Unit] = zio.ZIO$EffectTotal@65ccae4c

val finalized: IO[String, Unit] =
IO.fail("Failed!").ensuring(finalizer)
// finalized: IO[String, Unit] = zio.ZIO$CheckInterrupt@5071b0bd

The finalizer is not allowed to fail, which means that it must handle any errors internally.

Like try / finally, finalizers can be nested, and the failure of any inner finalizer will not affect outer finalizers. Nested finalizers will be executed in reverse order, and linearly (not in parallel).

Unlike try / finally, ensuring works across all types of effects, including asynchronous and concurrent effects.

Bracket

A common use for try / finally is safely acquiring and releasing resources, such as new socket connections or opened files:

val handle = openFile(name)

try {
processFile(handle)
} finally closeFile(handle)

ZIO encapsulates this common pattern with ZIO#bracket, which allows you to specify an acquire effect, which acquires a resource; a release effect, which releases it; and a use effect, which uses the resource.

The release effect is guaranteed to be executed by the runtime system, even in the presence of errors or interruption.

val groupedFileData: IO[IOException, Unit] = 
openFile("data.json").bracket(closeFile(_)) { file =>
for {
data <- decodeData(file)
grouped <- groupData(data)
} yield grouped
}

Like ensuring, brackets have compositional semantics, so if one bracket is nested inside another bracket, and the outer bracket acquires a resource, then the outer bracket's release will always be called, even if, for example, the inner bracket's release fails.

Next Steps

If you are comfortable with resource handling, then the next step is to learn about basic concurrency.