add sections abouzt ufcs and scope guards

This commit is contained in:
Johannes Loher 2018-04-14 01:32:30 +02:00
parent 757a4b8d49
commit 68c048ef5c

View file

@ -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
}
```
```
## 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 */
}