This commit is contained in:
Johannes Loher 2018-04-13 17:57:18 +02:00
parent dc0a64baa8
commit d15f34a530

View file

@ -24,22 +24,27 @@ echo "~/.dlang/dmd-2.079.0/activate" >> ~/.profile # Add dmd and dub to PATH on
* Download http://downloads.dlang.org/releases/2.x/2.079.0/dmd.2.079.0.dmg.
* Open `dmd.2.079.0.dmg`
* Run `DMD2.pkg` (you might need to activate the “allow installing applications from unverified developers” option in your security settings) and install with the default settings.
* Run `DMD2.pkg` (you might need to activate the “allow installing applications
from unverified developers” option in your security settings) and install with
the default settings.
#### Windows
* Download http://downloads.dlang.org/releases/2.x/2.079.0/dmd-2.079.0.exe.
* Run `dmd-2.079.0.exe` and install with the default settings (this will also install Visual Studio if you do not have it installed yet).
* Run `dmd-2.079.0.exe` and install with the default settings (this will also
install Visual Studio if you do not have it installed yet).
### Recommended editor setup
Visual Studio Code is the recommended editor, because it has the best D integration at the moment.
If you want to use another editor or IDE, that is perfectly fine.
However, instructions will only be provided for Visual Studio Code.
Visual Studio Code is the recommended editor, because it has the best D
integration at the moment. If you want to use another editor or IDE, that is
perfectly fine. However, instructions will only be provided for Visual Studio
Code.
#### Installation of Visual Studio Code
Download and install Visual Studio Code from here: https://code.visualstudio.com/. OS X users can also install it using Homebrew:
Download and install Visual Studio Code from here:
https://code.visualstudio.com/. OS X users can also install it using Homebrew:
```bash
brew tap caskroom/cask
@ -53,7 +58,8 @@ brew cask install visual-studio-code
|----------------|---------|
|OS X |⌘ + ⇧ + X|
|Windows |⌃ + ⇧ + X|
* Install the extension “D Programming Language (code-d)” (requires that git is installed).
* Install the extension “D Programming Language (code-d)” (requires that git is
installed).
* Restart Visual Studio Code.
## Basics
@ -71,9 +77,10 @@ void main() {
### Imports and modules
D has the concept of _modules_ and _packages_.
By importing a certain module with the `import` statement, all public symbols from module become available.
The standard library, called Phobos, is located in the `std` package.
E.g. in order to import the `file` module from Phobos, you would write:
By importing a certain module with the `import` statement, all public symbols
from module become available. The standard library, called Phobos, is located
in the `std` package. E.g. in order to import the `file` module from Phobos, you
would write:
```D
import std.file;
@ -81,7 +88,8 @@ import std.file;
#### Selective imports
It is possible (and often good style) to import symbols selectively from a module:
It is possible (and often good style) to import symbols selectively from a
module:
```D
import std.stdio: writeln, writefln;
@ -91,8 +99,9 @@ import std.stdio: writeln, writefln;
It is not necessary to place imports at the beginning of a file.
They can be located anywhere in the code.
If they appear inside a certain scope (delimited by braces), the imported symbols are only available inside that scope.
Here is an alternative version of the hello world program:
If they appear inside a certain scope (delimited by braces), the imported
symbols are only available inside that scope. Here is an alternative version of
the hello world program:
```D
void main()
@ -114,18 +123,20 @@ D has the following basic types:
| Datatypes | Size |
| ------------------------------- | ------------------------------------------------------------ |
| `bool` `byte`, `ubyte`, `char` | 8-bit |
| `bool`, `byte`, `ubyte`, `char` | 8-bit |
| `short`, `ushort`, `wchar` | 16-bit |
| `int`, `uint`, `dchar`, `float` | 32-bit |
| `long`, `ulong`, `double` | 64-bit |
| `real` | >= 64-bit (generally 64-bit, but 80-bit on Intel x86 32-bit) |
`char` represents UTF-8 characters, `wchar`represents UTF-16 characters, and `dchar` represents UTF-32 characters.
`char` represents UTF-8 characters, `wchar`represents UTF-16 characters, and
`dchar` represents UTF-32 characters.
#### Type conversion
For integer types, automatic type conversion is only allowed if no precision is lost (e.g. `int` to `long`).
All conversion between floating point types are allowed (e.g. `double` to `float`).
For integer types, automatic type conversion is only allowed if no precision is
lost (e.g. `int` to `long`). All conversion between floating point types are
allowed (e.g. `double` to `float`).
Manual type conversion is achieved with the `cast` expression:
@ -136,8 +147,9 @@ int b = cast(int) a;
#### Type properties
All types have a property `.init` to which variables of that type are initialized, if they are not initialized explicitly.
For integer types, this is `0` and for floating point types it is `nan`.
All types have a property `.init` to which variables of that type are
initialized, if they are not initialized explicitly. For integer types, this is
`0` and for floating point types it is `nan`.
Every type also has a `.stringof` property which yields its name as a string.
@ -162,7 +174,8 @@ And so do floating point types:
#### Indexing
For indexing, usually the alias type `size_t` is used, which is large enough to represent an offset into all addressable memory.
For indexing, usually the alias type `size_t` is used, which is large enough to
represent an offset into all addressable memory.
### Variable declarations
@ -184,8 +197,8 @@ It is also possible to declare several variables at once:
int myVar, someOtherVar;
```
D has automatic type deduction, so when explicitly initializing a variable, it is not necessary to mention the type.
Instead we can use the `auto` keyword:
D has automatic type deduction, so when explicitly initializing a variable, it
is not necessary to mention the type. Instead we can use the `auto` keyword:
```D
auto myVar = 42;
@ -197,6 +210,44 @@ Here is a combination of the above notations:
auto myInt = 42, myFloat = 4.2f;
```
### Mutability
Objects in D are mutable by default, but is possible to change this using
type qualifiers:
#### `immutable`
An object declared as `immutable` is enforced by the compiler to never change its
value.
```D
immutable int a;
a = 5; // error
```
`immutable` objects are implicitly shared accross threads, because the can never
change their value and thus race conditions are impossible.
#### `const`
`const` objects also can not be modified, but this is enforced only in the
current scope. This means, that the object could be modified from a different
scope. Both mutable and `immutable` objects implictly convert to `const`
objects:
```D
void foo(const char[] s)
{
// Do something with s
}
// Both calls are valid, thanks to const
foo("abcd"); // a string is an immutable array of char
foo("abcd".dup); // dup creates a mutable copy
```
Both `immutable` and `const` are transitive, i.e. the apply recursively to all
subcomponents of a type they are applied to.
### Functions
The basic syntax for functions is very similar to C:
@ -254,6 +305,219 @@ void fun() {
static assert(!__traits(compiles, fun_secret())); // fun_secret is not visible here
```
### Memory and pointers
D uses a garbage collector by default, but is also possible to do manual memory
management if needed.
D provides pointer types `T*` like in C:
```D
int a;
int* b = &a; // b contains address of a
auto c = &a; // c is int* and contains address of a
```
To allocate a new memory block on the garbage collected heap, use the `new`
operator:
```D
int* a = new int;
```
#### Memory safety
In general, pointer arithmetic like in C is allowed. This results in the usual
safety issues. To counter this, D defines 3 safety levels for functions:
`@safe`, `@trusted`, and `@system`. The default is `@system`, which gives no
safety guarantees. Functions annotated with `@safe` are only allowed to call
other `@safe` and `@trusted` functions and it is not possible to do pointer
arithmetic in them:
```D
void main() @safe {
int a = 5;
int* p = &a;
int* c = p + 5; // error
}
```
`@trusted` functions are functions that are manually verified to provide an
`@safe` interface. They create a bridge between `@safe` code and dirty low-level
code. Only use them very carefully!
### Structs
One way to create custom data types in D is with `struct`s:
```D
struct Person {
int age;
int height;
}
```
Unless created with the `new` operator, `sturct`s are always constructed on the
stack and copied _by value_ in assignments and as parameters to function calls.
```D
auto p = Person(30, 180);
auto t = p; // copy
```
You can also define a custom constructor:
```D
struct Person {
int age;
int height;
this(int age, int height) {
this.age = age;
this. height = height;
}
}
```
#### Member functions
`struct`s can have member functions. By default, they are `public` and
accessible from outside. By marking them `private`, you can limit access to
functions in the same module (different from C++ / Java etc.!):
```D
struct Person {
void doStuff() {
/* … */
}
private void privateStuff() {
/* … */
}
}
auto p = Person();
p.doStuff(); // call method doStuff
p.privateStuff(); // forbidden
```
#### `const` member functions
Member functions declared const can not modify any members. They can be called
on `immutable` and `const` objects.
#### `static` member functions
They work basically the same as in C etc.
### Arrays
D has two types of arrays, static arrays and dynamic arrays. Both of them are
bounds checked unless this feature is explicitly switched of with the compiler
flag `--boundcheck=off`.
#### Static arrays
Static arrays are stored on he stack or in static memory, depending on where
they are defined. They have a fixed, compile-time known length. The length is
part of the type:
```D
int[8] arr;
```
#### Dynamic arrays
Dynamic arrays are stored on the heap and have a variabe length, which can
change during runtime. A dynamic array is created with the new expression:
```D
auto size = 8;
int[] arr = new int[size];
```
The type `int[]` is called a _slice_ of `int`. Slices will be explained in more
detail in the next section.
Creating multidimensional arrays is also easy:
```D
auto matrix = new int[3][3];
```
#### Array operations and properties
Array concatenation is done with the `~` operator, which creates a new dynamic
array.
Mathematical operations can be applied to whole arrays using a syntax like
`c[] = a[] + b[]`, for example. This adds all elements of `a` and `b` so that
`c[0] = a[0] + b[0]`, `c[1] = a[1] + b[1]`, etc. It is also possible to perform
operations on a whole array with a single value:
```D
a[] *= 2; // multiple all elements by 2
a[] %= 26; // calculate the modulo by 26 for all a's
```
These operations can be optimized by the compiler using _SIMD_ instructions.
Both static and dynamic arrays provide the property `.length`, which is
read-only for static arrays, but can be used in the case of dynamic arrays to
change its size dynamically. The property .dup creates a copy of the array.
When indexing an array through the `arr[idx]` syntax, a special `$` symbol
denotes an array's length. For example, `arr[$ - 1]` references the last element
and is a short form for `arr[arr.length - 1]`.
### Slices
Slices are object of the type `T[]`for any given type `T. Slices provide a
_view_ to a subset of an array.
A slice consists basically of two members:
```D
T* ptr;
size_t length;
```
As we have already seen in the previous section, we can get slices by
allocating a new dynamic array:
```D
auto arr = new int[5];
assert(arr.length == 5)
```
The slice does not own the memory, it is managed by the garbage collector. The
slice is just a view on the memory.
You can also get slices to already existing memory:
```D
auto arr = new int[5];
auto newArr = arr;
auto smallerViewArr = arr[1 .. 4]; // index 4 is not included
assert(smallerViewArr.length == 3);
assert(newArr.length == 5);
smallerViewArr[0] = 10;
assert(newArr[1] == 10 && arr[1] == 10);
```
Again, it is important to keep in mind, that this is only a view to memory. No
memory is copied.
### Alias and `string`s
By using the `alias` statement , we can create new “names” for existing types:
```D
alias string = immutable(char)[];
```
This works very similar to `typedef` from C / C++.
The above definition of `string` is atually the definition that is used by D.
This means that `string`s are just mutable slices of `immutable` `char`s.
### Control flow
#### if…else
@ -272,7 +536,8 @@ if (a == 5) {
#### switch…case
Also very similar to how it is defined in other languages, but for it works for integer types, bools and strings (which will be covered later).
Also very similar to how it is defined in other languages, but for it works for
integer types, bools and strings (which will be covered later).
```D
string myString;
@ -304,13 +569,15 @@ switch(c) {
}
```
#### Loops
#### Old fashioned loops
`while`-, `do`…`while`- and classical `for`-loops all work the same as in C++/Java etc.
`while`-, `do`…`while`- and classical `for`-loops all work the same as in C++ /
Java etc.
##### Breaking out of outer loops
As usual, you can break out of a loop immediately by using the `break` keyword. Additionally, you can also break out of outer loops by using labels:
As usual, you can break out of a loop immediately by using the `break` keyword.
Additionally, you can also break out of outer loops by using labels:
```D
outer:
@ -321,3 +588,136 @@ for (int i = 0; i < 10; ++i) {
}
}
```
#### `foreach` loops
D has a `foreach` loops which allows for much better readable iterations.
##### Element iteration
We can easily iterate ofer slices using `foreach`:
```D
auto arr = new int[5];
foreach (e; arr) {
writeln(e);
}
```
##### Access by reference
By default the elements are copied during the iteration. If we want _in-place_
modification, we can use the `ref` qualifier:
```D
auto arr = new int[5];
foreach (ref e; arr) {
e = 5;
}
```
##### Iterate `n` times
It is easy to write iterations, which should be executed `n` times by using the
`..` syntax:
```D
foreach (i; 0 .. 3) {
writeln(i);
}
// prints 0 1 2
```
##### Iteration with index counter
For slices, it's also possible to access a separate index variable:
```D
foreach (i, e; [4, 5, 6]) {
writeln(i, ":", e);
}
// prints 0:4 1:5 2:6
```
### Ranges
Ranges are a very important concept for iteration in D. We can use `foreach`
loops, to iterate over ranges:
```D
foreach (element; range) {
// Loop body
}
```
If we use `foreach` with a range, this gets lowered to the compiler to something
similar to this:
```D
for (auto __rangeCopy = range; !__rangeCopy.empty; __rangeCopy.popFront()) {
auto element = __rangeCopy.front;
// Loop body...
}
```
This leads us to what ranges (or more specific `InputRange`s) actually are:
Anything, that implements the member functions needed by the above lowering:
```D
interface InputRange(E) {
bool empty() @property;
E front() @property;
void popFront();
}
```
However, ranges do not need to _implement_ such an interface in terms of
inheritance, they just have to provide the above member functions.
Typically, ranges are implemented as `struct`s (because most of the time, ranges
should be value types), but is also possible to implement them as `class`es,
which will be introduced later.
#### Laziness
Ranges are _lazy_. They won't be evaluated until requested. Hence, a range from
an infinite range can be taken:
```D
42.repeat.take(3).writeln; // [42, 42, 42]
```
#### Copying ranges
Copying a range by just using the assignment operator might not have the desired
effect, because iterationg over a range can be destructive (i.e. when the range
holds internal pointers and a deep copy would be necessary). “copyable” ranges
are called `ForwardRange`s. They need to implement a `.save` method which
returns a copy of the range:
```D
interface ForwardRange(E) : InputRange!E
{
typeof(this) save();
}
```
#### `RandomAccessRange`s
A `RandomAccessRange` is a `ForwardRange` which has a know `length` for which
each element can be access directly:
```D
interface RandomAccessRange(E) : ForwardRange!E
{
E opIndex(size_t i); // can access elements using range[i] syntax
size_t length() @property;
}
```
Slices are the most prominent example of `RandomAccessRange`s in
#### Lazy range algorithms
The D standard library provides a huge arsenal of lazy range algorithm
functions. Most of them can be found in in the `std.range` and `std.algorithm`
packages.