Enzo Soares
Publicado em 19 de Março de 2025 às 12:00
Saudações! Estou criando esse blog para mostrar o meu progresso aprendendo Zig, que é uma linguagem super interessante. Ela é bem baixo nível, então a alocação de memória é uma preocupação constante, mas a biblioteca padrão nos fornece ferramentas para lidar com isso.
Para começar, siga as informações de instalação aqui e adicione o executável ao seu PATH. Agora, para criar um projeto novo, é só digitar zig init!
Foi criado um arquivo main.zig, onde vai viver o seu código. Lá dentro, há uma função definida como:
pub fn main() void {
}
pub define que a função pode ser importada e void é simplesmente o tipo da função. Caso a função possa retornar algum erro, o tipo da função deve ser prefixado por !.
Para explicar vários dos conceitos da linguagem, vamos escrever um código simples que leia dois números de stdin e escreva em stdout a sua soma. O primeiro passo é importar a biblioteca std, que vai nos trazer todo tipo de mágica, e declarar a função main
const std = @import("std");
pub fn main() void {
}
Agora temos que declarar duas variáveis, uma para conter cada termo da adição, além de um buffer onde o conteúdo de stdin será armazenado. Além disso, precisamos definir o reader, que nada mais é que uma utilidade da biblioteca std para ler dados de stdin.
const std = @import("std");
pub fn main() void {
var buffer: [10]u8 = undefined;
const reader = std.io.getStdIn().reader();
var first: u8 = undefined;
var second: u8 = undefined;
}
Os valores e variáveis no Zig devem conter todos um tipo estático, definido no momento da compilação. Esse tipo pode ser implícito, como no caso do reader, ou explícito, como no caso do buffer, declarado após os dois pontos. first e second tem o tipo u8, o que quer dizer que são unsigned integers de 8 bits. buffer é um vetor de 10 u8, ou seja, 80 bits seguidos em memória. Como caracteres do alfabeto latino podem todos ser armazenados em apenas 8 bits cada, u8 serve como nosso tipo char.
Para ler de stdin, vamos usar esse fragmento, duas vezes:
var read_input = try reader.readUntilDelimiterOrEof(&buffer, '\n');
if (read_input) |user_input| {
first = try std.fmt.parseInt(u8, user_input, 10);
}
A função readUntilDelimiterOrEof lê stdin até encontrar a quebra de linha e coloca todos os valores que encontrar em read_input. buffer, em si, não é passado para a função, apenas a sua referência. Isso dá à função chamada a capacidade de modificar a variável, sendo usada para armazenar a entrada de dados.
Mas, se buffer é utilizado para armazenar o valor da input, por que temos que criar a nova variável read_input? A resposta é que buffer tem um tamanho fixo definido durante a compilação (no caso 10). Esse tamanho pode ser excessivo, então readUntilDelimiterOrEof retorna um novo buffer com o comprimento mínimo para armazenar o valor da input.
A linha if (read_input) |user_input| { merece uma atenção especial: o tipo de read_input não pode mudar depois que foi definido e ele foi definido implicitamente para ?u8[] já que é esse valor que a função readUntilDelimiterOrEof retorna. ?u8[] é uma união do tipo undefined e u8[], o que quer dizer que não pode ser utilizado por nenhuma outra chamada que espere receber ou undefined ou u8[]. A estrutura condicional colapsa a união apenas em u8[] e coloca esse valor na variável user_input.
Por último, devemos apenas escrever em stdout a soma dos dois valores, o que pode ser feito utilizando o método std.debug.print.
Para usá-lo, é necessário passar dois argumentos: O primeiro, que é a string de formatação, e o segundo, que são os valores que devem ser inseridos nessa string.
std.debug.print("O resultado é {d}", .{first+second});
A string de formatação deve conter letras fechadas por chaves para indicar o tipo da variável e onde ela deve ser posta. Já os valores a serem inseridos devem estar todos fechados por chaves e separados por vírgula, caso haja mais do que um. O resultado final é esse:
const std = @import("std");
pub fn main() !void {
var buffer: [10]u8 = undefined;
const reader = std.io.getStdIn().reader();
var first: u8 = undefined;
var second: u8 = undefined;
var read_input = try reader.readUntilDelimiterOrEof(&buffer, '\n');
if (read_input) |user_input| {
first = try std.fmt.parseInt(u8, user_input, 10);
}
read_input = try reader.readUntilDelimiterOrEof(&buffer, '\n');
if (read_input) |user_input| {
second = try std.fmt.parseInt(u8, user_input, 10);
}
std.debug.print("O resultado é {d}", .{first+second});
}
Para mais tutoriais, continue voltando :)