add sections abouzt ufcs and scope guards
This commit is contained in:
parent
757a4b8d49
commit
68c048ef5c
1 changed files with 65 additions and 1 deletions
|
@ -73,6 +73,9 @@
|
||||||
- [Anonymous functions and lambdas](#anonymous-functions-and-lambdas)
|
- [Anonymous functions and lambdas](#anonymous-functions-and-lambdas)
|
||||||
- [Exceptions](#exceptions)
|
- [Exceptions](#exceptions)
|
||||||
- [`nothrow`](#nothrow)
|
- [`nothrow`](#nothrow)
|
||||||
|
- [Gems](#gems)
|
||||||
|
- [Uniform function call syntax (UFCS)](#uniform-function-call-syntax-ufcs)
|
||||||
|
- [Scope guards](#scope-guards)
|
||||||
|
|
||||||
## Setup
|
## Setup
|
||||||
|
|
||||||
|
@ -1107,3 +1110,64 @@ int divide4By2() nothrow {
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 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 */
|
||||||
|
}
|
Loading…
Reference in a new issue