Python でいう with ブロックのようなもの
知りたいこと
Python でいう with ブロックのように、
- ブロック内の処理を実行
- 前後には決まった処理を入れる
という処理を自前で書きたい。
こういうやつ
py
with open(fname) as f:
...
概要
- C++ と Rust はスコープ内のオブジェクトが、スコープ終端で解放されるのでデストラクタ(相当)に解放処理などを実装する
- これを RAII という
それぞれの取っ掛かり用に用語だけ先に一覧しておく
- scoped cleanup / deterministic disposal(スコープ終端で確実に後始末)
- C++/Rust は RAII(destructor/Drop)
- それ以外は with/using/defer/ensure などで「確定的に」後始末
言語別でいうと、
- C++ : デストラクタ
- Rust : Drop (デストラクタ相当)
- Python : with / @contextmanager / enter / exit / yield
- Swift : defer / rethrows / @discardableResult
- Ruby : block_given? / yield / ensure
- C# : using / IDisposable / Dispose
- Go : defer
- Kotlin : use
それぞれの言語の例
気が向いた分だけ+粒度バラバラのまま
C++ 例
概念だけ記載
cpp
{
std::lock_guard<std::mutex> lock(m);
// ...
} // デストラクタが呼ばれる
- これを活用してリソース解放をするような手法を RAII という
Python 例
@contextmanager での実装例
py
from contextlib import contextmanager
class MyResource:
def __init__(self, path):
self.path = path
# ここで「獲得」(open/connect/lock など)
# 例: self.f = open(path, "rb")
def close(self):
# ここで「解放」(close/disconnect/unlock など)
# 例: self.f.close()
pass
@contextmanager
def open_resource(path):
res = MyResource(path)
try:
yield res
finally:
res.close()
# 使い方(Rubyのブロック版)
with open_resource("data.txt") as r:
# r を使う
pass
__enter__, __exit__ での実装例
py
class MyResource:
def __init__(self, path):
self.path = path
# ここで「獲得」(open/connect/lock など)
def close(self):
# ここで「解放」(close/disconnect/unlock など)
pass
def __enter__(self):
return self
def __exit__(self, exc_type, exc, tb):
self.close()
return False # 例外は握りつぶさない
# 使い方
with MyResource("data.txt") as r:
# r を使う
pass
Ruby 例
rb
class MyResource
def initialize(path)
@path = path
# ここで「獲得」(open/connect/lock など)
end
def close
# ここで「解放」(close/disconnect/unlock など)
end
def self.open(path)
res = new(path)
unless block_given?
return res
end
begin
yield res
ensure
res.close
end
end
end
MyResource.open("data.txt") do |r|
# r を使う
end
Swift
swift
final class MyResource {
let path: String
init(path: String) {
self.path = path
// ここで「獲得」(open/connect/lock など)
}
func close() {
// ここで「解放」(close/disconnect/unlock など)
}
}
@discardableResult
func withResource(_ path: String, _ body: (MyResource) throws -> Void) rethrows -> MyResource {
let res = MyResource(path: path)
defer {
res.close()
}
try body(res)
return res
}
try withResource("data.txt") { r in
// ...
}
以下広告