8.4 KiB
Hands-On DLang
Setup
Installing DMD and DUB
OS X
Installing with Homebrew (recommended)
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
- 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.
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).
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.
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, 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
).
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
-, 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:
outer:
for (int i = 0; i < 10; ++i) {
for (int j = 0; j < 5; ++j) {
/* … */
break outer; // breaks out of the outer loop
}
}