42 ms·
var m: std.Thread.Mutex = .{}; fn good() !void { m.lock(); defer m.unlock(); try f(); if (!everything_ok()) return; }
by AndyKelley 5y ago
var m: std.Thread.Mutex = .{};
fn good() !void {
m.lock();
defer m.unlock();
try f();
if (!everything_ok()) return;
}
It's still the RAII pattern. The explicit call to deinitialization does not disqualify it. I can overcomplicate it with a LockGuard class too:
const LockGuard = struct {
mutex: *std.Thread.Mutex,
fn init(mutex: *std.Thread.Mutex) !LockGuard {
mutex.lock();
return .{
.mutex = mutex,
};
}
fn deinit(lg: *LockGuard) void {
lg.mutex.unlock();
}
};
fn good() !void {
var lk = try LockGuard.init(&m);
defer lk.deinit();
try f();
if (!everything_ok()) return;
}
In this case it demonstrates an improvement over C++, which is what happens if an error happens during a constructor. In C++ throwing an exception in a constructor is essentially UB; in zig it's a regular function call just like anything else.
- lenkite 5y agoThanks for the informative snippet, but explicit expressions involving defer is not what RAII means. In RAII resource is automatically tied to object lifetime. https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization https://en.wikipedia.org/wiki/Resource_acquisition_is_initia... Regarding errors in a C++ constructor: https://isocpp.org/wiki/faq/exceptions#ctors-can-throw https://isocpp.org/wiki/faq/exceptions#ctors-can-throw