D doesn't require you to make wrapper types or write try/catch statements all the time. You can also use scope guards. They can be triggered by three conditions: exit
, success
, or failure
. Here, we'll perform a multistep transaction with scope guards to ensure exception safety.
In order to use scope guards to manage transactions, perform the following steps:
scope(success)
guard to commit the transaction immediately after starting it (if committing isn't implicit).scope(failure)
guard to roll back the transaction immediately after starting it. You may use multiple blocks to rollback multiple steps.scope(exit)
guard to free any resources. Write the free code immediately after the acquisition code. You may use multiple blocks to free multiple resources.{ // performing a SQL transaction database.query("START TRANSACTION"); scope(success) database.query("COMMIT"); scope(failure) database.query("ROLLBACK"); database.query(step_one); // these should throw on failure database.query(step_two); } { // performing a multi-part file download auto connection = Connection.acquire(); scope(exit) connection.close(); // release code immediatelyafter acquisition connection.download("file1"); scope(failure) std.file.remove("file1"); // undo codeimmediately after perform code connection.download("file2"); scope(failure) std.file.remove("file2"); }
D's scope guards are very useful to manage resources in an exception-safe way at the point of use. Each scope statement registers the code to be run on the given condition; success
if the function returns without throwing an exception, failure
if it exits by exception, or exit
, which is unconditional. The code is executed in the reverse order of registration and registration happens with regular flow control, as shown in the following code snippet:
scope(exit) writeln("Running scope exit"); scope(success) writeln("Running scope success"); return; scope(exit) writeln("This is never run since the function returned before it was registered.")
Running this code will print Running scope success
and then Running scope exit
.
The advantage of scope(success)
over putting the commit
code at the end of the function is that scope(success)
is written once, but always run, even if you have multiple exit points (return statements) from the function.
3.138.36.38