D
Экспресс-тур по языку D: типы, строки, циклы, массивы, ranges, шаблоны, классы и структуры, CTFE и контракты — весь язык на одной странице.
D — компилируемый системный язык со сборщиком мусора, который читается как Python, а работает как C++. Ниже весь язык за 16 минут: только код и комментарии.
Структура программы
Точка входа — main. Вывод — через модуль std.stdio.
// Однострочный комментарий
/* Блочный комментарий */
/+ Вложенный комментарий /+ можно внутри +/ +/
import std.stdio; // подключаем модуль ввода-вывода
// main может возвращать void или int
void main()
{
writeln("Привет, D!"); // writeln добавляет перевод строки
write("без перевода\n"); // write — без него
writefln("%d + %d = %d", 2, 3, 2 + 3); // форматированный вывод
}Переменные и типы
auto выводит тип, immutable и const запрещают изменение.
int i = 42; // целое (32 бита)
long big = 9_000; // 64 бита; _ — разделитель разрядов
double d = 3.14; // число с плавающей точкой
bool flag = true; // true / false
char c = 'A'; // один UTF-8 кодовый блок
auto x = 10; // тип выведен: int
auto y = 2.5; // тип выведен: double
immutable pi = 3.14159; // нельзя изменить никогда
const n = 100; // нельзя изменить через эту ссылку
// Все типы имеют значение по умолчанию (.init)
int zero; // = 0
double nan; // = NaN (не 0.0!)
int.max.writeln; // 2147483647 — свойства типов
double.epsilon.writeln;Строки
string — это immutable(char)[], то есть неизменяемый срез байтов UTF-8.
import std.string, std.uni, std.conv;
string s = "Hello";
string multi = `сырая строка: \n не экранируется`; // обратные кавычки
string wysiwyg = r"C:\path\file"; // r-строка тоже сырая
writeln(s.length); // длина в БАЙТАХ
writeln(s ~ " мир"); // ~ — конкатенация
writeln(s.toUpper); // HELLO
writeln(" тримминг ".strip);
writeln("a,b,c".split(",")); // ["a", "b", "c"]
writeln("42".to!int + 1); // 43 — строка -> число
// Интерполяция через format
import std.format;
writeln(format("x = %d, y = %.2f", 10, 2.5));Операторы и условия
final switch требует перечислить все случаи (проверка на этапе компиляции).
int a = 7;
if (a > 10)
writeln("много");
else if (a > 5)
writeln("средне");
else
writeln("мало");
// Тернарный оператор
auto sign = a >= 0 ? "плюс" : "минус";
// switch умеет строки и диапазоны
switch (a)
{
case 1: .. case 5: // диапазон 1..5
writeln("1-5");
break;
case 7:
writeln("семь");
break;
default:
writeln("другое");
}
// final switch — компилятор требует все enum-значения
enum Color { red, green, blue }
Color col = Color.green;
final switch (col)
{
case Color.red: writeln("красный"); break;
case Color.green: writeln("зелёный"); break;
case Color.blue: writeln("синий"); break;
}Циклы
foreach с диапазоном a..b — идиоматичный способ перебора чисел.
// Классический for
for (int k = 0; k < 3; k++)
write(k, " "); // 0 1 2
writeln;
// foreach по диапазону: 0,1,2,3,4
foreach (k; 0 .. 5)
write(k, " ");
writeln;
// foreach по массиву с индексом
auto arr = [10, 20, 30];
foreach (idx, val; arr)
writeln(idx, ": ", val);
// foreach_reverse — в обратном порядке
foreach_reverse (val; arr)
write(val, " "); // 30 20 10
writeln;
// while и do-while
int n = 3;
while (n > 0) { write(n, " "); n--; } // 3 2 1
do { writeln("хотя бы раз"); } while (false);Массивы и срезы
Срезы (slices) — это вид на участок памяти: указатель + длина, без копирования.
int[] dyn = [1, 2, 3]; // динамический массив
dyn ~= 4; // добавить элемент
dyn ~= [5, 6]; // добавить массив
writeln(dyn); // [1, 2, 3, 4, 5, 6]
writeln(dyn.length); // 6
int[3] fixed = [1, 2, 3]; // массив фиксированной длины (на стеке)
// Срезы: arr[начало .. конец] — конец не включается
writeln(dyn[1 .. 4]); // [2, 3, 4]
writeln(dyn[$ - 2 .. $]); // [5, 6] — $ это длина
// Срезы разделяют память!
auto part = dyn[0 .. 2];
part[0] = 99; // меняет и dyn[0]
// Ассоциативные массивы (хеш-карты)
int[string] ages;
ages["Аня"] = 25;
ages["Боб"] = 30;
writeln(ages["Аня"]); // 25
writeln("Аня" in ages); // указатель != null
foreach (name, age; ages)
writeln(name, " -> ", age);Функции
UFCS: x.f(y) эквивалентно f(x, y) — любую функцию можно звать как метод.
// Обычная функция
int add(int a, int b) { return a + b; }
// Значения по умолчанию
int inc(int x, int step = 1) { return x + step; }
// Несколько возвратов через кортеж (auto + tuple)
import std.typecons : tuple;
auto minMax(int[] xs)
{
return tuple(xs[0], xs[$ - 1]);
}
// UFCS: square(5) можно записать как 5.square
int square(int n) { return n * n; }
void demo()
{
writeln(add(2, 3)); // 5
writeln(inc(10)); // 11 (step по умолчанию)
writeln(5.square); // 25 — UFCS
writeln(5.square.inc); // 26 — цепочка
}
// Лямбды
auto twice = (int n) => n * 2;
writeln(twice(21)); // 42Структуры и классы
struct — значимый тип (копируется, на стеке). class — ссылочный тип (на куче, наследование, GC).
// struct: копируется по значению, нет наследования
struct Point
{
int x, y;
int sum() { return x + y; } // метод
}
Point p1 = Point(1, 2);
Point p2 = p1; // КОПИЯ
p2.x = 99; // p1.x не изменился
writeln(p1.sum()); // 3
// class: ссылочный тип, наследование, полиморфизм
class Animal
{
string name;
this(string name) { this.name = name; } // конструктор
string speak() { return "..."; }
}
class Dog : Animal // наследование через :
{
this(string name) { super(name); }
override string speak() { return "Гав!"; }
}
Animal a = new Dog("Рекс"); // ссылка на куче
writeln(a.name, ": ", a.speak()); // Рекс: Гав! (полиморфизм)Ranges и алгоритмы
Ranges — основа стандартной библиотеки. std.algorithm работает с любым range лениво.
import std.algorithm; // map, filter, reduce, sort, each
import std.range; // iota, take, chain
import std.array; // array — материализовать range
auto nums = [1, 2, 3, 4, 5, 6];
// map: преобразовать каждый элемент
auto sq = nums.map!(n => n * n);
writeln(sq); // [1, 4, 9, 16, 25, 36]
// filter: оставить подходящие
auto even = nums.filter!(n => n % 2 == 0);
writeln(even); // [2, 4, 6]
// reduce / fold: свернуть в одно значение
auto total = nums.reduce!((acc, n) => acc + n);
writeln(total); // 21
// Цепочки ленивы — вычисляются по требованию
auto result = nums
.filter!(n => n % 2 == 0)
.map!(n => n * 10)
.array; // .array — собрать в массив
writeln(result); // [20, 40, 60]
// iota — ленивый диапазон чисел
writeln(iota(1, 6).sum); // 15 (1+2+3+4+5)
writeln(iota(0, 100).take(3)); // [0, 1, 2]Шаблоны
Шаблоны параметризуются типами и значениями. Аргументы шаблона — в скобках после !.
// Шаблонная функция: T выводится автоматически
T max2(T)(T a, T b)
{
return a > b ? a : b;
}
writeln(max2(3, 7)); // 7 — T = int
writeln(max2(2.5, 1.0)); // 2.5 — T = double
writeln(max2!string("a", "b")); // явно T = string
// Шаблонная структура (как generic-контейнер)
struct Box(T)
{
T value;
T get() { return value; }
}
auto bi = Box!int(42);
auto bs = Box!string("hi");
writeln(bi.get(), " ", bs.get());
// Ограничения шаблона: только для числовых типов
import std.traits : isNumeric;
T doubleIt(T)(T x) if (isNumeric!T)
{
return x * 2;
}
writeln(doubleIt(21)); // 42Управление памятью
По умолчанию работает сборщик мусора (GC). scope(exit) гарантирует очистку при выходе из блока.
import std.stdio;
void memory()
{
// new выделяет память в куче под управлением GC
auto data = new int[1000]; // GC освободит сам
// scope(exit) — выполнится при выходе из области видимости
scope(exit) writeln("всегда при выходе");
scope(success) writeln("если без исключения");
scope(failure) writeln("если было исключение");
// Ручное управление: malloc/free из core.stdc.stdlib
// для кода без GC помечают функцию как @nogc
}
// @nogc — компилятор запрещает любые GC-аллокации внутри
@nogc int pureAdd(int a, int b)
{
return a + b; // никаких new, ~, замыканий
}
// @safe / @trusted / @system — уровни безопасности памяти
@safe int safeFn() { return 1; } // запрещены опасные операцииОсобенности: CTFE и контракты
CTFE — выполнение функций на этапе компиляции. Контракты (in/out/invariant) проверяют условия.
// CTFE: обычная функция считается в compile-time
int factorial(int n)
{
return n <= 1 ? 1 : n * factorial(n - 1);
}
enum fact5 = factorial(5); // ВЫЧИСЛЕНО при компиляции = 120
static immutable table = factorial(6); // тоже compile-time
// static if — ветвление на этапе компиляции
void info(T)()
{
static if (is(T == int))
writeln("это int");
else
writeln("другой тип");
}
// Контракты функции: in (предусловие), out (постусловие)
int sqrtInt(int x)
in { assert(x >= 0, "нужно неотрицательное"); }
out (result) { assert(result * result <= x); }
do {
import std.math : sqrt;
return cast(int) sqrt(cast(double) x);
}
// Инвариант структуры — проверяется после каждого метода
struct Account
{
int balance;
invariant { assert(balance >= 0, "баланс не может быть отрицательным"); }
}
// unittest — встроенные тесты, запускаются флагом -unittest
unittest
{
assert(factorial(5) == 120);
assert(sqrtInt(9) == 3);
}