4 ms·
Note that in `good` below, one can have many such lock guards: lk1, lk2, etc without any explicit lock release expression in the body of the function. std:
by lenkite 5y ago
Note that in `good` below, one can have many such lock guards: lk1, lk2, etc without any explicit lock release expression in the body of the function.
std::mutex m;
void good()
{
std::lock_guard<std::mutex> lk(m); // RAII class: mutex acquisition is initialization
f(); // if f() throws an exception, the mutex is released
if(!everything_ok()) return; // early return, the mutex is released
} // if good() returns normally, the mutex is released
- AndyKelley 5y agovar 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