Scala2.13のUsingを試してみた(ローンパターン)

#Scalaのリソース解放処理
##~ 2.12
2.12までのScalaには、Javaの[try-with-resources](https://docs.oracle.com/javase/jp/7/technotes/guides/language/try-with-resources.html)やC#の[using](https://docs.microsoft.com/ja-jp/dotnet/csharp/language-reference/keywords/using-statement)のような、外部リソースを自動的に解放するための関数・構文がありませんでした。
そのため、都度finalyを書きたくなければ、以下のように自動解放処理を自前で実装する必要がありました(ローンパターン)。
[Scala using（ローンパターン）](http://www.ne.jp/asahi/hishidama/home/tech/scala/sample/using.html)
[Scalaで一番よく使うローンパターン](https://qiita.com/piyo7/items/c9be1f39bcfea43a778a)
[Loanパターンをモナドfor式で使えるようにしてみたよ](https://gakuzo.hatenadiary.org/entry/20110630/1309442452)

##2.13 ~
Scala 2.13.0からは標準ライブラリに`scala.util.Using`オブジェクトが追加されたことで、上記のような実装を追加せずともローンパターンが使えるようになりました。
[scala.util.Using](https://www.scala-lang.org/api/2.13.0/scala/util/Using%24.html)

#scala.util.Usingを使ってみる
##a. 単一リソースの場合
`Using(【リソース】)(【処理内容】)`の形で記述します。

```sample.txt
hello
world
```

```Main.scala
import scala.io.Source
import scala.util.Using

object Main extends App {
  Using(Source.fromFile("sample.txt")) { source => // ここのsourceはSource.fromFile("sample.txt")の戻り値
    for (line <- source.getLines) println(line)
  }
}
```

実行結果

```
hello
world
```

上記では`scala.io.Source`を使用しましたが、リソースは`java.lang.AutoCloseable`の任意のサブクラスを指定可能です。

```Main.scala
import java.io.{BufferedReader, FileReader}
import scala.util.Using

object Main extends App {
  // AutoCloseableのサブクラスなら何でも可
  Using(new BufferedReader(new FileReader("sample.txt"))) { br =>
    var line = ""
    while ({ line = br.readLine(); line ne null }) {
      println(line)
    }
  }
}
```

##b. 複数リソースの場合
`Using.Manager`オブジェクトを使い、下記のように記述します。

```sample.txt
hello
world
```

```sample2.txt
using
sample
```

```Main.scala
import java.io.{BufferedReader, FileReader}
import scala.util.Using

object Main extends App {
  Using.Manager { use =>
    // 引数use(実体はUsing.Managerクラスのインスタンス)を介すことでリソースを複数指定可能
    val br1 = use(new BufferedReader(new FileReader("sample.txt")))
    val br2 = use(new BufferedReader(new FileReader("sample2.txt")))

    var line1 = ""
    var line2 = ""
    while ({
      line1 = br1.readLine()
      line2 = br2.readLine()
      (line1 ne null) && (line2 ne null)}) {
      println(line1)
      println(line2)
    }
  }
}
```

実行結果

```
hello
using
world
sample
```

###※複数リソース使用時の注意点
`Using.Manager`で複数リソースを管理する場合、必ず対象のリソースに`use`関数を適用させるか、今までどおり自前で解放処理を記述してください。
以下のように`use`なしでもコンパイルは通りますが、リソースが自動で解放されないためリソースリークの原因になりえます。

```scala
Using.Manager { use =>
  val br1 = use(new BufferedReader(new FileReader("sample.txt")))
  // 自動リソース解放対象にならない(コンパイルは通る)
  val br2 = new BufferedReader(new FileReader("sample2.txt"))

...
```

##戻り値(Try[A])の使用
`Using`および`Using.Manager`は戻り値として`Try[A]`を返すため、下記のように変数に代入することも可能です。[^1]

```Main.scala
import scala.io.Source
import scala.util.{Failure, Success, Try, Using}

object Main extends App {
  val result: Try[List[String]] = Using(Source.fromFile("sample.txt"))(_.getLines.toList)

  result match {
    case Success(xs) => xs.foreach(println)
    case Failure(_)  => println("failure")
  }
}
```

実行結果

```
hello
world
```

失敗した場合

```Main.scala
import scala.io.Source
import scala.util.{Failure, Success, Try, Using}

object Main extends App {
  val result: Try[List[String]] = Using(Source.fromFile("sample.txt")) { source =>
    throw new Exception  // 強制的に失敗させる
    source.getLines.toList
  }

  result match {
    case Success(xs) => xs.foreach(println)
    case Failure(_)  => println("failure")
  }
}
```

実行結果

```
failure
```

#まとめ
Scalaにusing相当の機能がないことは前から言われていたので、標準で追加されて安心しました。
今後は特に理由がなければ、自前で実装せずにこちらを使っていけばいいのではないかと思います。

最後までお読み頂きありがとうございました｡
質問や不備についてはコメント欄か[Twitter](https://twitter.com/ka2_kamaboko)までお願いします。

[^1]: ここでは`getLines.toList`と記述していますが、この`toList`を入れなかった場合、戻り値は`Try[Iterator[String]]`となります。これでもコンパイルは通りますが、実行すると要素の読み取りが既にリソースが閉じられた後になってしまい、例外が発生してしまいます。