ZIG - Hello, world!
In Zig già il semplice saluto che introduce lo studio di tutti i
linguaggi, ci permette delle interessanti considerazioni per iniziare ad
addentrarci nelle pieghe di questo linguaggio. Ovviamente non tutto potrà
essere charo da subito ma è il prezzo da pagare in queste fasi
introduttive, speicalmente quando c'è un linguaggio he ha l'ambizione di
proporre delle novità. Tutto, piano piano, diventerà più chiaro.
Vediamo un primo esempio.
Esempio 2-1
const std = @import("std");
pub fn main() !void {
try std.fs.File.stdout().writeAll("Hello world!\n");
}
Definiamo nella prima riga una costante di nome std che importa la libreria
standard std. Questo nome non può più essere cambiato essendo definito appunto
costante. Questo tipo di definizione è obbligatoria per il linguaggio. La
@ che
trovate prima di import indica che si tratta di funzioni built-in ovvero sono
primitive per il linguaggio, riconosciute e gestite direttamente dal
compilatore.
pub fn main() !void - questa istruzione ci presenta l'entry
point del programma, una funzione, introdotta tramite la keyword
fn e dal nome
obbligatorio main che deve essere marcata pubblica come ci indica
pub. Il
compilatore lo impone pena un errore in compilazione. Da notare quel
!void che è
interessante e non significa come potrebbe sembrare per chi viene da altri
linguaggi, "non vuoto" ma vuol dire che la funzione non restituisce nulla oppure
fallisce. In pratica il ! esclamativo davanti ad un tipo (!T) significa che o si
avrà quel tipo o un errore. Quindi
!T significa o si ha un risultato di
tipo T o un errore. Nello specifico si parla di error union type. Esempi vari:
!u8 = o un valore u8, o un errore
!void = o niente (void), o un errore
!?i32 = o un ?i32 (opzionale), o un errore
il ? introduce quindi un valore
opzionale ma avremo modo di riparlarne.
try
std.fs.File.stdout().writeAll("hello world!\n"); - siamo al core di questo
programma. In questa versione di Hello World il programma ci ha detto che
l'operazione potrebbe fallire o non restituire nulla. Attraverso
try esso è in
grado restituire l'eventuale errore verso il runtime che potrà esporlo. Se
togliete il try il compilatore si lamenta che non è in grado di gestire
l'error-union che abbiamo visto prima.
error: error union is ignored
std come detto è la libreria standard. Essa contiene una infinità di
strutture e namespace e uno di questi namespace è fs. Siamo quindi arrivati a
std.fs laddove il punto è l'operatore che ci permette di navigare nelle
gerarchie degli elementi del type system di Zig. Il nostro fs sta per file
system. All'interno di questo namespace abbiamo diverse strutture ( di cui
parleremo in seguito) e una di queste, lo avrete già capito, è
File. Siamo
quindi arrivati a capire
std.fs.File che, come detto è una struttura che
ha un metodo statico ovvero stdout() che propone in output un elemento (in
realtà una istanza di File) su cui applicare la funzione
writeAll. A sua volta
questa espone una stringa che in Zig, come in molto altri linguaggi, è una
sequenza di caratteri all'interno di una coppia di doppi apici.
Come
avrete certamente notato, in Zig, come in molti linguaggi si usano le parentesi
graffe per racchiudere i blocchi di istruzioni, siano esse una o più.
Inoltre è
da osservare le singole istruzioni vengono chiuse da un ; (punto e virgola).
Il metodo che abbiamo visto è un metodo pratico che esprime molto bene, se
vogliamo, la natura elaborata di questo linguaggio. E' possibile però usare una
strada più semplice e lo vediamo nel codice seguente:
Esempio 2.2
const std = @import("std");
pub fn main() !void {
std.debug.print("Hello, World!\n", .{});
}
Questa seconda via cambia sostanzialmente l'operatore di scrittura che usiamo. Ancora una volta si parte, come indica la prima riga, dal nostro std ma utilizziamo il namespace debug. Questo al suo interno, ha una funzione pronta all'uso che si chiama print e che, nota importante, scrive non sullo standard output ma sullo standard error. In casi semplici come i nostri i due canali di output visivamente coincidono ma in realtà sono due flussi diversi. Questo secondo esempio è molto semplice ed immediato, ma come suggerisce l'uso di quella parolina "debug" non è un sistema da usare in produzione proprio perchè fa uso di canale di output che non quello tipicamente deputato alla visualizzazione del risultato di un programma funzionante. Visto però che è semplice e molto usato per i test lo adopereremo spesso nell'ambito del nostro percorso. Ad ogni modo è interessante notare l'istruzione alla terza riga in particolare quel .{ }. L'istruzione di stampa ha due argomenti: il primo è una stringa il secondo è una ennupla (o tuple che viene indicata proprio con la sequenza .{ } e argomento che sarà trattato a suo tempo) che potrebbe (in questo caso no) contenere degli elementi da inserire in appositi segnaposto. Vediamo un esempio di codice che usa tali segnaposto:
Esempio 2-3
const std = @import("std");
pub fn main() !void {
std.debug.print("Ciao, {s}!\n", .{"amico"});
}
Potete notare quindi quel {s} con il quale informiamo il compilatore che
in quella posizione deve trovare una sequenza di caratteri. Dedicheremo un
po' di tempo a quei segnaposto ed al loro contenuto.
Non abbiamo però
ancora sviscerato completamente il discorso della stampa. Un terzo metodo
parte ancora da std e torniamo un po' al
primo punto usando writer invece di
writeAll.
Esempio 2.4
const std = @import("std");
pub fn main() !void {
var stdout_buffer: [1024]u8 = undefined;
var stdout_writer = std.fs.File.stdout().writer(&stdout_buffer);
const stdout = &stdout_writer.interface;
try stdout.print("Hello, Zig!\n", .{});
try stdout.flush();
}
Sarà meglio tornare su questo esempio, fornito a puro titolo
esemplificativo, quando avremo appreso altri concetti.
Come detto, da qui
in avanti useremo il tranquillo std.debug.print.
Prima di lasciarci, due note importanti.
Per prima cosa ricordate
che Zig è un linguaggio case-sensitive. Ovvero fa
distinzione fra maiuscole e minuscole. Scrivere "pippo" o "Pippo" o
"piPPo" equivale a definire 3 entità completamente distinte tra loro.
Seconda cosa, importante per la manutenzione dei vostri programmi,
parliamo dei commenti. In breve, vale la seguente tabella:
| Tipo | Sintassi | Note |
|---|---|---|
| Commento singola riga |
// |
Il più usato |
| Commento multilinea |
/* ... */ |
Supporta annidamento |
| Commento doc |
/// |
Per documentazione |