After opening a file, someone has to close it.
Kotlin has the convenient use { ... }
method for the case where you're doing all the work with the file in-place.
But sometimes, you have to do some other things with the file and return another object which gains ownership.
Currently, I am doing something like this:
fun open(path: Path, fileMode: FileMode): StoreFile {
var success = false
val channel = FileChannelIO.open(path, fileMode)
try {
if (channel.size == 0 && fileMode == FileMode.READ_WRITE) {
writeInitialContent(channel)
}
val result = StoreFile(channel)
success = true
return result
} finally {
if (!success) {
channel.close()
}
}
}
writeInitialContent
could fail, in which case I would have to close the file. If it doesn't fail, then StoreFile
takes ownership and becomes the thing which the caller has to close.
This is a common kind of nastiness in Java, and the way I've had to write it in Kotlin isn't really any cleaner than Java, and with the way it's currently written, there's still the potential for double-closing if StoreFile
itself also happens to close the file on failure.
You hit the same basic problem even trying to buffer a stream, which is also fairly common. In this case, I don't know whether buffered()
will throw, and if it does, nobody closes the file. In this case, buffered()
itself could deal with the ugliness, but I checked its code and it does not.
fun readSomething(path: Path): Something {
path.inputStream().buffered().use { stream ->
// ...
}
}
Is there a better way to structure this?