language | filename | contributors |
---|---|---|
D |
learnd.d |
/*TODO:
* static variables and CTFE (also TLS)
* delegates, function/delegate literals
* DDOC sections, macros
* value/alias template parameters
* mixins
*/
// Single-line comments start with //
/* Multi-line comments look
like this. */
/+ Or they look like this. /++/ comments differ from /**/ comments in that they
can be nested. Which makes them the tool of choice for commenting out code. +/
// Import modules with 'import'.
import std.stdio; // std -> standard; io -> input/output
// All statements must end with a semicolon.
/* The program's entry point is a function called main. In its simplest form
it has no parameters and has a return type of 'void' which means it doesn't
return anything. */
void main()
{ // start of the function definition
int luckyNumber; // Declaring the variable 'luckyNumber' of type 'int'.
luckyNumber = 42; // Assigning a value.
int unluckyNumber = 13; // Declaring and assigning (initializing) in one step.
// Can use 'auto' instead of an explicit type when an initializer is present:
auto unluckyNumberAgain = unluckyNumber;
// Simple output is done via writeln ("write line") from std.stdio:
writeln("Your lucky number for today: ", luckyNumber);
///////////////////////////////////////
// Boolean Logic
///////////////////////////////////////
bool b = true;
b = !b; // logical negation => false
b = false || true; // logical or => true
b = false && true; // logical and => false
///////////////////////////////////////
// Integers
///////////////////////////////////////
// with sign
byte y; // 8 bits, aka 1 byte
short h; // 16 bits
int n; // 32 bits
long o; // 64 bits
/* For each signed integral type there is an unsigned one of the same size. The
names just have a "u" put in front. */
ubyte uy;
ushort us;
uint un;
ulong uo;
/* size_t has the same range as the address space,
i.e. unsigned 32 or 64 bits, depending on what machine you're compiling for. */
size_t st;
// Literals
n = 42; // decimal
n = 0x2A; // hexadecimal
n = 0b101010; // binary
// You can add underscores as separators for readability where you see fit.
o = 42_000_000_000; // easily recognizable as 42 billions
n = 0xFF_FF_F0_00; // 4 bytes, obviously
/* A literal's type defaults to int. When the value is too big for int, it's
long. You can use suffixes when you need to be explicit about a literal's
type. */
auto v1 = 42L; // long
auto v2 = 42U; // uint
auto v3 = 42UL; // ulong
// Arithmetic
n = 2 + 3; // addition => 5
n = 2 - 3; // subraction => -1
n = 2 * 3; // multiplication => 6
n = 2 / 3; // division => 0, because integer division rounds towards zero
n = 2 % 3; // modulo => 1
n = 2 ^^ 3; // exponentation => 8
n += 3; // shorthand for n = n + 3;
// You can do that with the other operators as well, of course.
// Comparison Operators
b = 3 == 2; // equal? false
b = 3 != 2; // not equal? true
b = 3 > 2; // greater than? true
b = 3 < 2; // less than? false
b = 2 <= 2; // less than or equal? true
b = 2 >= 2; // greater than or equal? true
///////////////////////////////////////
// Floating Point
///////////////////////////////////////
float f; // 32 bits
double d; // 64 bits
real r; // largest size the hardware supports, but at least 64 bits
d = 0.000_042;
d = 42e-6; // scientific notation
f = 1.0 / 10; // about 0.1
/* Otherwise similar to integer arithmetic. (Not specific to D, floating point
arithmetic in general has many subtle oddities; be aware.) */
///////////////////////////////////////
// Arrays
///////////////////////////////////////
int[] darr1; // a dynamic array of ints
darr1 = [1, 2, 3];
assert(darr1.length == 3);
/* assert is a builtin that throws an AssertError if the passed expression is
false. */
auto darr2 = darr1;
darr1[0] = 42; // Setting the first element to 42.
assert(darr2[0] == 42); // darr2 refers to the same data as darr1.
darr1 ~= 4; // append 4
assert(darr1 == [42, 2, 3, 4]);
assert(darr2 == [42, 2, 3]); // didn't affect darr2
/* The append operation copied darr1's data to a new location. darr1 no longer
refers to the same data as darr2: */
darr1[0] = 1;
assert(darr2[0] == 42);
// You can explicitly do such a copy:
auto darr3 = darr1.dup;
darr1[0] = 42;
assert(darr3[0] == 1);
// Quite different beasts are static (aka fixed-sized) arrays:
int[3] sarr1 = [1, 2, 3]; // a static array of 3 ints
// The length must be known at compile time.
// They are value types:
int[3] sarr2 = sarr1;
sarr1[0] = 42;
assert(sarr2[0] == 1);
// They cannot grow or shrink.
// You can get a dynamic array from a static one via the slicing operator:
int[] darr4 = sarr1[]; // darr4 refers to sarr1's data
/* Be careful with this, though. Static array variables live on the stack.
Dynamic views of their data are invalidated when the function returns. */
///////////////////////////////////////
// const And immutable
///////////////////////////////////////
const(int)[] carr; // The elements cannot be mutated through this variable.
immutable(int)[] iarr; // The elements cannot be mutated at all.
// Both unqualified (i.e. mutable) and immutable implicitly convert to const:
carr = darr1;
carr = iarr;
///////////////////////////////////////
// Text (Characters And Strings)
///////////////////////////////////////
char c = 'd'; // a UTF8 code unit
wchar wc = 'd'; // ("wide char") a UTF16 code unit
dchar dc = 'd'; // ("double wide char") a UTF32 code unit
// A dchar value is the same as the Unicode code point.
// Strings are arrays of immutable UTF code units. Same prefixes as with char.
string s; // string is an alias of immutable(char)[], i.e. they're synonymous
wstring ws; // ditto with wchar
dstring ds; // ditto with dchar
// Literals
c = 'a';
c = '\t'; // The usual escape sequences are understood.
assert("
" == "\n" || "
" == "\r\n"); // Actual newlines are allowed.
assert("aä" == "a\xC3\xA4"); // \x.. is a UTF8 byte
assert("aä" == "a\u00E4"); // \u.... is a 4 hexdigits Unicode code point
assert("aä" == "a\U000000E4"); // \U........ is a 8 hexdigits Unicode code point
/* Escape sequences are not recognized in 'WYSIWYG' (What You See Is What You
Get) string literals: */
assert(r"\n" == "\\n");
assert(`\n` == "\\n"); // alternative WYSIWYG syntax
// You can make your own aliases:
alias MyNameForByte = byte;
alias ubyte MyOtherNameForByte; // older, now somewhat less popular syntax
///////////////////////////////////////
// Control Structures
///////////////////////////////////////
if(false)
{
writeln("I am never run.");
}
else if(false) // Can omit braces with single statements.
writeln("I am also never run.");
else writeln("I print.");
switch(n)
{
case 0: .. case 9: writeln("less than ten"); break;
case 10: case 11: writeln("still not a dozen"); break;
case 12: writeln("a dozen exactly"); break;
default: writeln("a lot"); break;
}
// The following snippets all print "0, 1, 2, 3, 4, 5, 6, 7, 8, 9, ":
int i = 0;
while(i < 10)
write(i++, ", "); // i++ increments i in-place, after using its value.
writeln();
int j = 0;
do
write(j, ", ");
while(++j < 10); // ++j increments j in-place, before using its value
writeln();
for(int k = 0; k < 10; k++)
write(k, ", ");
writeln();
foreach(k; 0 .. 10)
write(k, ", ");
writeln();
///////////////////////////////////////
// Bitwise Operators
///////////////////////////////////////
ubyte u = 0b0000_0001;
assert(~u == 0b1111_1110); // bitwise negation
assert((0b1100 & 0b1010) == 0b1000); // bitwise AND
assert((0b1100 | 0b1010) == 0b1110); // bitwise (inclusive) OR
assert((0b1100 ^ 0b1010) == 0b0110); // bitwise XOR (eXclusive)
assert(1 << 2 == 0b100); // bitwise left shift (by 2)
assert(0b100 >> 1 == 0b10); // bitwise right shift (by 1)
///////////////////////////////////////
// Pointers
///////////////////////////////////////
int* p; // a pointer to (i.e. the memory address of) an int
void* pv; // a generic pointer to data of unknown type
// Use & to get a pointer to something:
int[2] stuff = [1, 2];
p = &stuff[0];
// Put a * in front to de-reference a pointer and retrieve the pointed-to value.
assert(*p == 1);
// Arithmetic
p += 1; // add the width of 1 int, i.e. 4
assert(*p == 2);
} // end of the main function
///////////////////////////////////////
// Functions
///////////////////////////////////////
/*
return type
| function name
| | parameter type
| | | parameter name
| | | | */
int twice(int x)
{
return 2 * x;
}
/* D's 'unittest' blocks are compiled in when you set some compiler switch
(-unittest for dmd, the reference compiler). They're run before the main
function. */
unittest
{
assert(twice(7) == 14);
}
/* There are various attributes that can (and should) be put on functions:
'pure' guarantees that the function does not rely on or modify data other
than the passed arguments.
'nothrow' guarantees that the function does not throw any Exceptions.
'@safe' guarantees memory safety (e.g. forbids pointer arithmetic). */
int plus(int a, int b) pure nothrow @safe
{
return a + b;
}
unittest
{
/* There's an alternative function call syntax: UFCS, which stands for
Uniform Function Call Syntax. It's "Uniform" because it looks like builtin
properties and method calls.
You put the first argument and a dot before the function name; the rest of
the arguments go in the parentheses as usual: */
assert(1.plus(2) == plus(1, 2));
}
/++ Documentation comments like this are part of the language (DDOC).
Their left delimiters have an extra slash/asterisk/plus; i.e.:
/// like this
/** or like this */
/++ or like this +/
+/
void demonstrateRefAndOut(int i, ref int r, out int o) pure nothrow @safe
{
i = 2; // i is passed by value, so this only affects the local variable.
r = 2; /* r is passed by reference, so this affects the variable at the call
site, too. */
/* The variable passed as o is reset to its initial value before passing it
to the function by reference. */
assert(o == int.init);
o = 2;
}
unittest
{
int i = 1;
int r = 1;
int o = 1;
assert(o != int.init);
demonstrateRefAndOut(i, r, o);
assert(i == 1);
assert(r == 2);
assert(o == 2);
}
/* Functions can take compile time (template) parameters. The parameter list
goes before the list of runtime parameters. */
T templated(T)(T arg)
{
// 'static if' is evaluated at compile time.
/* 'is()' expressions are quite complex. This form simply tests if two
types are the same. */
static if(is(T == int)) return arg + 1;
else static if(is(T == float)) return arg - 1;
else return T.init;
}
unittest
{
assert(templated(42) == 43); /* T is implicitly set from the argument.
This is called Implicit Function Template Instantiation (IFTI). */
assert(templated!(float)(42) == 41); // T is explicitly set to float.
assert(templated!byte(42) == byte.init);
// Can omit the parentheses with a simple single template argument.
}
/* An alternative implementation using template specializations instead of the
static if chain: */
T templated2(T : int)(T arg) {return arg + 1;}
T templated2(T : float)(T arg) {return arg - 1;}
T templated2(T)(T arg) {return T.init;}
unittest
{
assert(templated2!int(42) == 43);
assert(templated2!float(42) == 41);
assert(templated2!byte(42) == 0);
}
/* Yet another variant, this time using template constraints.
Also demonstrating the 'auto' return type, meaning it's deduced from the
definition. */
auto templated3(T)(T arg) if(is(T == int)) {return arg + 1;}
auto templated3(T)(T arg) if(is(T == float)) {return arg - 1;}
auto templated3(T)(T arg) if(!(is(T == int) || is(T == float))) {return T.init;}
unittest
{
assert(templated3!int(42) == 43);
assert(templated3!float(42) == 41);
assert(templated3!byte(42) == 0);
}
///////////////////////////////////////
// Structs And Classes
///////////////////////////////////////
// Structs are collections of data.
struct MyStruct
{
int x, y;
float z = 4.2;
}
unittest
{
MyStruct s = MyStruct(42, 43); // Sets x and y, leaves z at the default.
s.z = 44; // Access members with the dot operator.
assert(s.x == 42 && s.y == 43 && s.z == 44);
MyStruct* h = new MyStruct(42, 43);
// 'new' constructs on the heap and returns a pointer.
h.z = 44; // The dot operator automatically dereferences.
assert(h.x == 42 && h.y == 43 && h.z == 44);
}
// Like functions, structs (and classes and interfaces) can be templated.
struct Rectangle(T)
{
T width, height;
/* Structs can have associated functions (methods).
'const' guarantees that this method won't mutate the data. */
auto area() const pure nothrow @safe
{
return width * height;
}
}
unittest
{
alias Rect = Rectangle!int;
auto r = Rect(2, 3);
assert(r.area() == 6);
}
/* Classes are similar to structs, but their natural habitat is the heap,
they are reference types, and they have inheritance. */
class A
{
/* 'private' members are accessible from the current module only.
They are non-virtual. */
private string data;
/* 'protected' members are also accessible from derived classes.
'abstract' methods are to be overridden by derived classes. */
abstract protected void appendFoobar() pure nothrow @safe;
// They can still have implementations (to be used by the derived classes).
abstract protected void prependFoobar() pure nothrow @safe
{
data = "Foobar" ~ data;
}
// 'public' members are accessible from anywhere.
public bool startsWithFoobar() const pure nothrow @safe
{
return data[0 .. "Foobar".length] == "Foobar";
}
/* 'public' is the default, so you can omit the keyword.
'final' methods cannot be overridden. They are non-virtual. */
final endsWithFooar() const pure nothrow @safe
{
return data[$ - "Foobar".length .. $] == "Foobar";
}
// Classes need constructors to allow passing arguments on construction.
this(string data) pure nothrow @safe
{
this.data = data;
}
this() {} // also allowing construction without arguments
}
unittest
{
A a;
assert(a is null);
a = new A("..."); // Must construct with 'new'.
assert(!a.startsWithFoobar());
auto a2 = a;
a2.prependFoobar();
assert(a.startsWithFoobar()); // a and a2 refer to the same object.
}
class B : A // B inherits from (aka extends) A.
{
override void appendFoobar() pure nothrow @safe
{
data ~= "Foobar";
}
override void prependFoobar() pure nothrow @safe
{
data = "!" ~ data;
super.prependFoobar();
}
override bool startsWithFoobar() const pure nothrow @safe
{
return super.startsWithFoobar() && data["Foobar".length] == '!';
}
}
/* A class can only inherit from one base class, but it can implement multiple
interfaces. */
interface I
{
string shoot() pure nothrow @safe;
}
interface J
{
string poot() pure nothrow @safe;
// Interfaces can have final methods with implementations.
final string root() pure nothrow @safe
{
return "/";
}
}
class C : B, I, J
{
string shoot() {return "pew pew";}
string poot() {return "ppffrr";}
}
unittest
{
I i = new C;
assert(i.shoot == "pew pew");
J j = new C;
assert(j.poot == "ppffrr");
assert(j.root == "/");
}
///////////////////////////////////////
// Templates
///////////////////////////////////////
// The template concept can be used independently from functions, etc.
template typeVariations(T)
{
alias Const = const(T);
alias Immutable = immutable(T);
}
unittest
{
alias V = typeVariations!int;
static assert(is(V.Const == const(int)));
static assert(is(V.Immutable == immutable(int)));
}
///////////////////////////////////////
// enum
///////////////////////////////////////
// An enum is a list of static (compile time) values.
enum E {a = 1, b, c}
unittest
{
assert(E.a = 1);
// When you don't explicitly assign a value, it's the previous value + 1.
assert(E.b == E.a + 1);
assert(E.c == E.b + 1);
}
/* You can omit the enum name. The values are then put into the surrounding
scope. */
enum {a, b}
unittest {assert(b == a + 1);}
enum e = 42; // A single value is also called a "manifest constant".
http://dlang.org is the official website. When you have questions, don't fear asking in the beginner section of the forum.
The D Programming Language (TDPL) by Andrei Alexandrescu is the authoritative book on D.
D Programming Language Tutorial by Ali Çehreli is free.
Very nice document, but it looks like
static
class members and functions not mentioned here