handson-dlang/hands-on_dlang.md
2018-04-13 15:02:06 +02:00

8.4 KiB

Hands-On DLang

Setup

Installing DMD and DUB

OS X

brew install dmd
brew install dub
Installing locally using the install script
curl -fsS https://dlang.org/install.sh | bash -s dmd
echo "~/.dlang/dmd-2.079.0/activate" >> ~/.profile # Add dmd and dub to PATH on starting a bash shell
Installing using the installer

Windows

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:

brew tap caskroom/cask
brew cask install visual-studio-code

Extension setup

  • Open the Extension view in the sidebar:
    Operating system Shortcut
    OS X ⌘ + ⇧ + X
    Windows ⌃ + ⇧ + X
  • Install the extension “D Programming Language (code-d)” (requires that git is installed).
  • Restart Visual Studio Code.

Basics

Hello World

import std.stdio;

void main() {
    writeln("Hello World");
}

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:

import std.file;

Selective imports

It is possible (and often good style) to import symbols selectively from a module:

import std.stdio: writeln, writefln;

Scoped imports

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:

void main()
{
import std.stdio: writeln;
writeln("Hello World");
}
/* writeln is not available outside of the main function */

Imports match files and directories

The module system is entirely based on files. E.g. my.thing refers to a file thing.d in the folder my/.

Basic Types

D has the following basic types:

Datatypes Size
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, wcharrepresents 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).

Manual type conversion is achieved with the cast expression:

long a = 1;
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.

Every type also has a .stringof property which yields its name as a string.

Integer types have some more properties:

Property Description
.max The maximum value the type can hold
.min The minimum value the type can hold

And so do floating point types:

Property Description
.max The maximum value the type can hold
.min_normal The smallest representable normalized value that is not 0
.nan NaN value
.infinity Infinity value
.dig number of decimal digits of precisions
.mant_dig number of bits in mantissa

Indexing

For indexing, usually the alias type size_t is used, which is large enough to represent an offset into all addressable memory.

Variable declarations

Variables are declared by writing the type followed by the variable name:

int myVar;

They can also be explicitly initialized:

int myVar = 42;

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:

auto myVar = 42;

Here is a combination of the above notations:

auto myInt = 42, myFloat = 4.2f;

Functions

The basic syntax for functions is very similar to C:

int add(int lhs, int rhs) {
    return lhs + rhs;
}

Return type deduction

A functions return type can be defined to be auto. In this case, the return type will be infered. Multiple return statements are possible, but must return compatible types.

auto add(int lhs, int rhs) { // returns `int`
    return lhs + rhs;
}

auto lessOrEqual(int lhs, int rhs) { // returns `double`
    if (lhs <= rhs)
        return 0;
    else
        return 1.0;
}

Default arguments

Those also work the same as in C and other languages:

void plot(string msg, string color = "red") {
    /* ... */
}
plot("D rocks");
plot("D rocks", "blue");

Local functions

It is possible to define functions locally (even inside other functions). Those functions are not visible outside their parents scope.

void fun() {
    int local = 10;
    int fun_secret() {
        local++; // that's legal
    }
    /* … */
}
static assert(!__traits(compiles, fun_secret())); // fun_secret is not visible here

Control flow

if…else

Very similar to how it is defined in other languages:

if (a == 5) {
    writeln("Condition is met");
} else if (a > 10) {
    writeln("Another condition is met");
} else {
    writeln("Nothing is met!");
}

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).

string myString;
/* … */
switch(myString) {
    case "foo":
        writeln(`Cool, myString was "foo"`);
        break;
    default:
        writeln("Meh, myString was something boring");
        break;
}

For integer types, it is also possible to define ranges:

int c = 5;
switch(c) {
    case 0: .. case 9:
        writeln(c, " is within 0-9");
        break; // necessary!
    case 10:
        writeln("A Ten!");
        break;
    default: // if nothing else matches
        writeln("Nothing");
        break;
}

Loops

while-, dowhile- 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:

outer:
for (int i = 0; i < 10; ++i) {
    for (int j = 0; j < 5; ++j) {
        /* … */
        break outer; // breaks out of the outer loop
    }
}