Scala: try-with-resources

Intro

A usual pattern for working with files (or other resources), for example, is following:

val file: InputStream = _
try {
// read from stream
} finally {
file.close()
}

More plot into a subject

It’s quite common to use IOUtils.closeQuietly for this matter:

var file: InputStream = _
try {
// read from stream
} finally {
IOUtils.closeQuietly(file)
}

try-with-resources

In Java, since Java7, there is a special language construct for such cases, called try try-with-resources. This concept described very well in this tech article. Actually, most of the content of this post is scattered there (not everything, thankfully).

Mistake 1: swallow exception in finally

As I mentioned in the first example, such code isn’t good:

finally {
file.close()
}
var exception: Throwable = null
val file: InputStream = _
try {
// read from stream
} catch {
case e =>
exception = e
throw e
} finally {
try {
file.close()
} catch {
case fe =>
if (exception != null) {
exception.addSuppressed(fe)
} else {
throw fe
}
}
}

Mistake 2: Catching Exception/Throwable

Another thing that people don’t pay attention is a catch clause. What can be wrong with it? Actually, a lot.

def cleanly[A, B](resource: A)(cleanup: A => Unit)(doWork: A => B): Try[B] = {
try {
Success(doWork(resource))
} catch {
case e: Exception => Failure(e)
}
finally ...
case e: Exception => Failure(e)
case NonFatal(e) => Failure(e)

Mistake 3: Not closing resource

In previous part I suggested to use NonFatal extractor, but it has to be used wisely. Another example from Google. In attempt to make try-with-resources more idiomatic, the big mistake was introduced:

def apply[C <: Closeable, R](resource: => C)(f: C => R): Try[R] =
Try(resource).flatMap(resourceInstance => {
try {
val returnValue = f(resourceInstance)
Try(resourceInstance.close()).map(_ => returnValue)
} catch {
case NonFatal(exceptionInFunction) =>
try {
resourceInstance.close()
Failure(exceptionInFunction)
} catch {
case NonFatal(exceptionInClose) =>
exceptionInFunction.addSuppressed(exceptionInClose)
Failure(exceptionInFunction)
}
}
})

Mistake 4: Swallowing exceptions from close

I showed, in the beginning, a use of closeQuietly in finally. When can it be bad? Let's put aside the topic of swallowing exceptions in general. Let's assume, that it's either ok or we do proper logging on the swallow. So, what can go wrong?

val file = new FileOutputStream("file.bin")
try {
file.write(1)
} finally {
try {
file.close()
} catch {
case NonFatal(e) => // log it properly!
}
}
  • Some internal error during closing a file descriptor.

try-with-resources in Scala

So, how the OK version of try-with-resources in Scala may look like?

def withResources[T <: AutoCloseable, V](r: => T)(f: T => V): V = {
val resource: T = r
require(resource != null, "resource is null")
var exception: Throwable = null
try {
f(resource)
} catch {
case NonFatal(e) =>
exception = e
throw e
} finally {
closeAndAddSuppressed(exception, resource)
}
}

private def closeAndAddSuppressed(e: Throwable,
resource: AutoCloseable): Unit = {
if (e != null) {
try {
resource.close()
} catch {
case NonFatal(suppressed) =>
e.addSuppressed(suppressed)
}
} else {
resource.close()
}
}

Recap

Let’s recap the main points:

  • Be cautious in finally clause, don’t let exception there to overthrow an original one.
  • You may swallow exceptions in close for read operations.

Conclusion

Despite the fact that resource management is a well-known subject, there are still many mistakes around it. Especially in languages not mature enough (I believe, Scala is one of that kind). Don’t get me wrong, there are libraries in Scala world that handle resource management properly, i.e. better-files or scala-arm. But I believe that it should be a part of the language. Either as a language construct or as a part of a scala-library. It’s too important to not have it.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Dmitry Komanov

Dmitry Komanov

Software developer, moved to Israel from Russia, trying to be aware of things.