From 68c048ef5c2b9dcb71a22689ec8a3e0123cc76a5 Mon Sep 17 00:00:00 2001 From: Johannes Loher Date: Sat, 14 Apr 2018 01:32:30 +0200 Subject: [PATCH] add sections abouzt ufcs and scope guards --- hands-on_dlang.md | 66 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 65 insertions(+), 1 deletion(-) diff --git a/hands-on_dlang.md b/hands-on_dlang.md index c9b0594..6a4e049 100644 --- a/hands-on_dlang.md +++ b/hands-on_dlang.md @@ -73,6 +73,9 @@ - [Anonymous functions and lambdas](#anonymous-functions-and-lambdas) - [Exceptions](#exceptions) - [`nothrow`](#nothrow) + - [Gems](#gems) + - [Uniform function call syntax (UFCS)](#uniform-function-call-syntax-ufcs) + - [Scope guards](#scope-guards) ## Setup @@ -1106,4 +1109,65 @@ int divide4By2() nothrow { return divide(4, 2); // error, divide my throw } -``` \ No newline at end of file +``` + +## Gems + +### Uniform function call syntax (UFCS) + +_UFCS_ is a simple and yet very powerful feature of D. It basically allows +that any call to a free function `fun(a)` can be written as member function +call `a.fun()`. If the compiler sees `a.fun()` and the type of `a` does not +have a member function `fun()`, it tries to find a global function whose +first parameter matches the type of `a`. + +This makes complex chains of function calls much more readably. Instead of +writing + +```D +foo(bar(a)); +``` + +one can write + +```D +a.bar().foo(); +``` + +For functions that take no arguments, it is not necessary to use parenthesis, so any function like that can be used like a property: + +```D +import std.uni: toLower; +auto a = "Cool Stuff".toLower; +assert(a == "cool stuff"); +``` + +UFCS is especially important when dealing with ranges where several algorithms can be put together to perform complex operations, still allowing to write clear and manageable code. + +```D +import std.algorithm : group; +import std.range : chain, retro, front, retro; +[1, 2].chain([3, 4]).retro; // 4, 3, 2, 1 +[1, 1, 2, 2, 2].group.dropOne.front; // tuple(2, 3u) +``` + +### Scope guards + +Scope guards allow executing statements at certain conditions if the current block is left: +* `scope(exit)` will always call the statements. +* `scope(success)` statements are called when no exceptions have been thrown. +* `scope(failure)` denotes statements that will be called when an exception + has been thrown before the block's end. + +Using scope guards makes code much cleaner and allows to place resource allocation and clean up code next to each other. They also improve safety because they make sure certain cleanup code is always called independent of which paths are actually taken at runtime. + +Scope guards are called in the reverse order they are defined. + +```D +void foo() { + import core.stdc.stdlib : free, malloc; + int* p = cast(int*) malloc(int.sizeof); + scope(exit) free(p); + /* Do some stuff, which potentially might throw, which does not matter, + p is freed anyways when leaving the scope */ +} \ No newline at end of file