Функции и параметры
Разбираем, как Zig передаёт параметры и почему аргументы неизменяемы.
Параметры функции в Zig передаются по значению и неизменяемы внутри тела: чтобы менять данные вызывающего, передают указатель явно.
Функция в Zig объявляется через fn, принимает типизированные параметры и возвращает значение указанного типа. На первый взгляд всё как в C, но есть важные нюансы: параметры неизменяемы, а большие значения компилятор может передавать эффективно сам.
Базовое объявление
const std = @import("std");
fn add(a: i32, b: i32) i32 {
return a + b;
}
pub fn main() void {
std.debug.print("{d}\n", .{add(2, 3)});
}
Вывод:
5
Сигнатура читается слева направо: имя, параметры с типами, затем тип возврата. Если функция ничего не возвращает, пишут void.
Параметры неизменяемы
fn tryChange(x: i32) void {
// x += 1; // ОШИБКА: параметр неизменяем
const y = x + 1; // так можно: новая локальная переменная
_ = y;
}
Внутри функции параметр нельзя изменить — он ведёт себя как const. Это убирает класс ошибок, когда функция случайно правит «свой» аргумент. Если нужно менять — заведите локальную копию или принимайте указатель.
Изменение через указатель
fn increment(p: *i32) void {
p.* += 1; // меняем значение по указателю вызывающего
}
var n: i32 = 10;
increment(&n); // n станет 11
Чтобы функция изменила данные вызывающего, она принимает указатель *i32, а вызывающий передаёт адрес через &n. Это явный «выходной параметр»: глядя на сигнатуру, видно, что функция может что-то изменить.
Передача структур и срезов
Большие структуры разумно передавать по указателю, чтобы избежать копирования. Срезы передают по значению — это дёшево, ведь срез сам по себе лёгкий (указатель + длина). Для последовательностей почти всегда принимают []const T: он работает с массивом любой длины.
Как работает под капотом
Хотя семантически параметры передаются по значению, компилятор Zig вправе передать большую структуру по скрытому указателю, если это эффективнее и неотличимо для программиста (ведь менять её всё равно нельзя). Это оптимизация без изменения смысла. Неизменяемость параметров позволяет компилятору смело так поступать.
Частые ошибки
Первая — пытаться изменить параметр и удивляться ошибке: заведите локальную копию или принимайте указатель. Вторая — передавать огромную структуру по значению ради «чистоты» и терять на копировании, когда указатель уместнее. Третья — забыть & при передаче адреса в функцию, ожидающую указатель.
Итог
- Функции объявляют через
fn; тип возврата — после списка параметров. - Параметры передаются по значению и неизменяемы внутри тела.
- Чтобы менять данные вызывающего, принимают указатель и передают адрес через
&. - Срезы передают по значению (они лёгкие), большие структуры — обычно по указателю.