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.