LEARN X · ЗА 14 МИН

V

Экспресс-тур по языку V (vlang) за 14 минут: переменные, типы, циклы, структуры, опции и результаты — весь язык в комментариях кода.

V (vlang) — простой компилируемый язык со статической типизацией, синтаксисом в духе Go и упором на безопасность: нет null, нет неявных глобальных переменных, неизменяемость по умолчанию. Весь язык — на одной странице, в комментариях к рабочему коду.

Структура программы

Точка входа — функция main. Файлы компилируются командой v run файл.v.

// Однострочный комментарий
/* Многострочный
   комментарий */

// fn main — точка входа программы.
// Если файл запускают через `v run`, main можно опустить —
// тогда код верхнего уровня выполняется как скрипт.
fn main() {
	println('Привет, V!') // вывод строки с переносом
	print('без переноса')  // вывод без \n
}

Переменные

Объявление через :=. По умолчанию переменная неизменяема; для изменения нужен mut.

fn main() {
	x := 10        // неизменяемая переменная (тип int выведен автоматически)
	// x = 20      // ОШИБКА компиляции: x нельзя менять

	mut y := 5     // mut — изменяемая переменная
	y = 7          // ок
	y += 3         // ок, теперь 10

	a, b := 1, 2   // множественное присваивание
	println('$x $y $a $b')

	// Все переменные обязаны использоваться,
	// иначе компилятор выдаст ошибку.
}

Типы

Числа, логический тип, строки и символы Unicode.

fn main() {
	i := 42          // int   — целое (обычно 32 бита)
	big := i64(99)   // i64   — 64-битное целое
	f := 3.14        // f64   — число с плавающей точкой
	flag := true     // bool  — true / false
	s := 'строка'    // string
	r := `Я`         // rune  — один символ Unicode (в обратных кавычках)

	// Явное приведение типов вызовом-конструктором:
	n := int(f)      // 3 (дробная часть отбрасывается)
	g := f64(i)      // 42.0

	println('$i $big $f $flag $s $r $n $g')
}

Строки

Интерполяция через $, форматирование через ${...}, богатый набор методов.

fn main() {
	name := 'V'
	age := 5

	// Интерполяция: простая переменная — через $имя
	println('Язык $name, возраст $age')
	// Выражение и форматирование — через ${...}
	println('Через 10 лет: ${age + 10}')
	println('Число: ${3.14159:.2f}') // 3.14

	s := 'Hello, World'
	println(s.len)               // длина: 12
	println(s.to_upper())        // HELLO, WORLD
	println(s.to_lower())        // hello, world
	println(s.contains('World')) // true
	println(s.replace('l', 'L')) // HeLLo, WorLd
	println(s.split(', '))       // ['Hello', 'World']
	println(s[0..5])             // срез: Hello

	// Сырые строки (без обработки \n и $):
	raw := r'без \n и $интерполяции'
	println(raw)
}

Операторы и условия

if/else — выражение (возвращает значение). match заменяет switch.

fn main() {
	a, b := 7, 3
	println(a + b) // 10
	println(a % b) // 1 (остаток)
	println(a > b && b > 0) // && и || — логические

	// if/else как выражение — возвращает значение:
	max := if a > b { a } else { b }
	println('max = $max')

	// match — мощный аналог switch:
	grade := 4
	msg := match grade {
		5 { 'отлично' }
		4 { 'хорошо' }
		3 { 'удовлетворительно' }
		else { 'другое' } // else обязателен, если перечислены не все случаи
	}
	println(msg)

	// match с диапазонами и списками:
	n := 15
	cat := match n {
		0 { 'ноль' }
		1...9 { 'одна цифра' } // диапазон через ...
		else { 'много' }
	}
	println(cat)
}

Циклы

В V есть только один цикл — for, но во многих формах.

fn main() {
	// 1. Классический for со счётчиком:
	for i := 0; i < 3; i++ {
		print('$i ') // 0 1 2
	}
	println('')

	// 2. for как while (условие):
	mut k := 0
	for k < 3 {
		k++
	}

	// 3. Бесконечный for (как while true):
	mut n := 0
	for {
		n++
		if n >= 3 {
			break    // выход из цикла
		}
		continue // переход к следующей итерации
	}

	// 4. for in — обход коллекции:
	for x in [10, 20, 30] {
		print('$x ') // 10 20 30
	}
	println('')

	// for in с индексом:
	for idx, val in ['a', 'b'] {
		println('$idx -> $val')
	}

	// for in по диапазону:
	for j in 0 .. 3 {
		print('$j ') // 0 1 2
	}
	println('')
}

Массивы и мапы

Массивы типизированы и могут расти; мапы — пары ключ-значение.

fn main() {
	// Массив (все элементы одного типа):
	mut nums := [1, 2, 3]
	nums << 4            // добавить элемент (теперь [1, 2, 3, 4])
	println(nums.len)   // 4
	println(nums[0])    // 1 (индексация с нуля)
	println(nums.last()) // 4
	println(2 in nums)  // true — проверка наличия

	// Массив с заранее заданной длиной:
	zeros := []int{len: 3} // [0, 0, 0]
	println(zeros)

	// Методы высшего порядка:
	even := nums.filter(it % 2 == 0) // [2, 4]; it — текущий элемент
	doubled := nums.map(it * 2)      // [2, 4, 6, 8]
	println('$even $doubled')

	// Мапа map[ключ]значение:
	mut ages := map[string]int{}
	ages['Аня'] = 25
	ages['Боб'] = 30
	println(ages['Аня'])      // 25
	println('Боб' in ages)    // true
	println(ages.keys())      // ['Аня', 'Боб']

	// Литерал мапы:
	colors := {
		'красный': '#f00'
		'синий':   '#00f'
	}
	println(colors['синий'])
}

Функции

Объявление через fn. Тип возврата — после списка аргументов. Поддерживается множественный возврат.

// Аргументы: имя тип; возвращаемый тип — после скобок
fn add(a int, b int) int {
	return a + b
}

// Множественный возврат:
fn min_max(arr []int) (int, int) {
	mut mn := arr[0]
	mut mx := arr[0]
	for x in arr {
		if x < mn { mn = x }
		if x > mx { mx = x }
	}
	return mn, mx
}

// Аргументы передаются неизменяемыми; для изменения — mut:
fn increment(mut x []int) {
	x << 0
}

fn main() {
	println(add(2, 3)) // 5

	lo, hi := min_max([3, 1, 9, 4])
	println('min=$lo max=$hi') // min=1 max=9

	mut data := [1, 2]
	increment(mut data) // mut указывается и при вызове
	println(data)       // [1, 2, 0]
}

Структуры

struct группирует данные; методы привязываются через получателя; есть встраивание (embedding).

struct Point {
	mut:
	x int
	y int
}

// Метод: (p Point) — получатель. mut — если меняем поля.
fn (p Point) sum() int {
	return p.x + p.y
}

fn (mut p Point) move(dx int, dy int) {
	p.x += dx
	p.y += dy
}

// Встраивание (embedded struct) — аналог наследования:
struct Point3D {
	Point // встроенная структура: поля x, y доступны напрямую
	z int
}

fn main() {
	mut p := Point{x: 1, y: 2} // создание с именованными полями
	println(p.sum())           // 3
	p.move(10, 10)
	println('${p.x}, ${p.y}')   // 11, 12

	q := Point3D{
		Point: Point{x: 1, y: 2}
		z: 3
	}
	println(q.x)       // 1 — поле встроенной структуры
	println(q.sum())   // 3 — её метод тоже доступен
}

Опции и результаты

Главная фишка V: вместо исключений — типы ?Type (опция: значение или none) и !Type (результат: значение или ошибка).

// ?int — функция возвращает int ИЛИ none
fn find_even(arr []int) ?int {
	for x in arr {
		if x % 2 == 0 {
			return x
		}
	}
	return none // явное «значения нет»
}

// !int — функция возвращает int ИЛИ error
fn safe_div(a int, b int) !int {
	if b == 0 {
		return error('деление на ноль') // вернуть ошибку
	}
	return a / b
}

fn main() {
	// or {} — что делать, если none/ошибка:
	v := find_even([1, 3, 5]) or { -1 }
	println(v) // -1

	// if разворачивает опцию в переменную:
	if e := find_even([1, 4, 5]) {
		println('нашли чётное: $e') // нашли чётное: 4
	}

	// Обработка ошибки результата:
	r := safe_div(10, 0) or {
		println('ошибка: $err') // err — встроенная переменная с текстом
		0
	}
	println(r) // 0

	// Проброс ошибки вверх через ! (внутри функции, возвращающей !):
	// res := safe_div(10, 2)!
}

Модули и импорт

Каждый файл принадлежит модулю; внешние модули подключаются через import.

// module main — модуль по умолчанию для исполняемых программ.
module main

import math      // импорт модуля стандартной библиотеки
import os        // работа с ОС
// import strings // ещё пример

fn main() {
	println(math.sqrt(16.0)) // 4.0 — обращение через имя модуля
	println(math.max(3, 7))  // 7

	// Аргументы командной строки:
	println(os.args)

	// Чтобы функция/структура была видна из других модулей —
	// её объявляют с pub:
	// pub fn hello() { ... }
}

Особенности V

Ключевые принципы языка, делающие код безопаснее.

fn main() {
	// 1. НЕТ null/nil для обычных значений.
//    Отсутствие значения выражается через ?Type и none —
//    компилятор заставляет обработать оба случая.

	// 2. Неизменяемость по умолчанию.
	//    Переменные, аргументы и поля структур immutable,
	//    пока явно не помечены mut. Меньше неожиданных мутаций.
	x := 5
	// x = 6 // ОШИБКА

	// 3. НЕТ глобальных переменных (по умолчанию).
	//    Состояние передаётся явно через аргументы —\n//    это упрощает рассуждение о коде и тестирование.

	// 4. Все переменные и импорты должны использоваться,
	//    иначе — ошибка компиляции. Код остаётся чистым.

	// 5. Обязательная обработка ошибок: значение типа !T
	//    нельзя проигнорировать — нужен or {} или проброс !.

	// 6. Единый форматтер: `v fmt` приводит весь код
	//    к одному стилю, как gofmt в Go.
	println('V: безопасность по умолчанию, x = $x')
}

Дальше: официальная документация docs.vlang.io и сам компилятор на GitHub.

Поддержать проект