scalaintermediate

Resource Management with Using

Manage resources safely with Using, try-with-resources, bracket pattern, and Cats Resource.

scala
import scala.util.Using
import scala.util.{Try, Success, Failure}
import java.io.*
import java.net.{Socket, ServerSocket}

// Using: automatic resource management
def readFile(path: String): Try[String] =
  Using(scala.io.Source.fromFile(path)) { source =>
    source.getLines().mkString("\n")
  }

def writeFile(path: String, content: String): Try[Unit] =
  Using(new PrintWriter(new FileWriter(path))) { writer =>
    writer.print(content)
  }

// Multiple resources with Using.Manager
def copyFile(src: String, dst: String): Try[Long] =
  Using.Manager { use =>
    val in = use(new FileInputStream(src))
    val out = use(new FileOutputStream(dst))
    in.transferTo(out)
  }

// Custom resource with AutoCloseable
class ConnectionPool(name: String, size: Int) extends AutoCloseable:
  println(s"  Pool '$name' opened with $size connections")
  private var closed = false

  def getConnection: String =
    if closed then throw IllegalStateException("Pool is closed")
    s"conn-$name-${System.nanoTime() % 1000}"

  def close(): Unit =
    closed = true
    println(s"  Pool '$name' closed")

// Bracket pattern (functional)
def bracket[R, A](acquire: => R)(release: R => Unit)(use: R => A): Try[A] =
  val resource = acquire
  try
    val result = use(resource)
    release(resource)
    Success(result)
  catch
    case e: Exception =>
      try release(resource) catch case _: Exception => ()
      Failure(e)

// Resource composition
class Database(url: String) extends AutoCloseable:
  println(s"  Connected to $url")
  def query(sql: String): List[String] = List(s"row1-$sql", s"row2-$sql")
  def close(): Unit = println(s"  Disconnected from $url")

class Cache(name: String) extends AutoCloseable:
  println(s"  Cache '$name' initialized")
  private val store = scala.collection.mutable.Map.empty[String, String]
  def get(key: String): Option[String] = store.get(key)
  def put(key: String, value: String): Unit = store(key) = value
  def close(): Unit = println(s"  Cache '$name' cleared")

@main def run(): Unit =
  // Write then read
  val tmpFile = File.createTempFile("test", ".txt")
  tmpFile.deleteOnExit()

  writeFile(tmpFile.getPath, "Hello, resource management!") match
    case Success(_) => println("Written successfully")
    case Failure(e) => println(s"Write error: ${e.getMessage}")

  readFile(tmpFile.getPath) match
    case Success(content) => println(s"Read: $content")
    case Failure(e) => println(s"Read error: ${e.getMessage}")

  // Connection pool
  println("\nConnection Pool:")
  Using(ConnectionPool("main", 10)) { pool =>
    val c1 = pool.getConnection
    val c2 = pool.getConnection
    println(s"  Got: $c1, $c2")
  }

  // Multiple resources
  println("\nMultiple Resources:")
  Using.Manager { use =>
    val db = use(Database("postgres://localhost/mydb"))
    val cache = use(Cache("app-cache"))

    val rows = db.query("SELECT * FROM users")
    rows.foreach(r => cache.put(r, r.toUpperCase))
    println(s"  Cached ${rows.size} rows")
    println(s"  Lookup: ${cache.get(rows.head)}")
  } match
    case Success(_) => println("All resources cleaned up")
    case Failure(e) => println(s"Error: ${e.getMessage}")

  // Bracket pattern
  println("\nBracket Pattern:")
  bracket(ConnectionPool("bracket", 5))(_.close()) { pool =>
    pool.getConnection
  } match
    case Success(conn) => println(s"  Got: $conn")
    case Failure(e) => println(s"  Error: ${e.getMessage}")

Use Cases

  • Safe file handling
  • Database connection management
  • Resource cleanup guarantees

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.