ZIG - Il tipo Optional
In Zig, un optional rappresenta un valore che può essere presente oppure assente in qualche modo può essere visto come l'equivalente di null in altri linguaggi, ma in modo type-safe. In pratica rappresentano un valore di un dato tipo T oppure null.
La definizione formale è molto semplice, basta anteporre un ? davanti al
tipo di dato che vorremmo fosse contenuto nella variabile o nella costante
che andamo a definire:
Quindi un tipo ?T è un union speciale con due stati:
--
null
-- un valore di tipo T
Il valore null è un singleton del tipo
Null. Va evidenziato che il tipo ?T è distinto da T.
NB: gli Optional non sono da confondere con gli Error Union
di cui parleremo un apposita sezione.
Come esempi pratici possiamo ad esempio scrivere cose tipo:
var x: ?i32 = 42; // optional con valore
var y: ?i32 = null; //
optional vuoto
Vediamo un piccolo programma di prova:
Esempio 19.1
const std = @import("std");
pub fn main() !void {
const x: ?i32 = 10;
if (x) |v| {
std.debug.print("Valore: {}\n", .{v});
} else {
std.debug.print("Null!\n", .{});
}
}
In cui gestiamo in due casi in cui può trovarsi la costante x ovvero contenere un valore di tipo i32 oppure essere null.
Usare if-else è la strada più comune per estrarre e gestire il valore da un Optional. Un altro modo fa uso della keyword orelse:
Esempio 19.2
const std = @import("std");
pub fn main() !void {
const a: ?i32 = null;
const b = a orelse 30; // b = 30
std.debug.print("valore {}\n", .{b});
const c: ?i32 = 20;
const d = c orelse 40; // d = 20
std.debug.print("valore {}", .{d});
}
Con questo output:
| valore 30 valore 20 |
In pratica una scrittura tipo:
const y = x orelse 0;
equivale a:
if x == null
usa 0
else
usa il valore di x
Come si può notare non ci sono complessi costruttori o altro, anzi, uno degli
aspetti che rende gli Optional di Zig particolarmente eleganti è che
si integrano direttamente nella
sintassi del linguaggio (if,
while,
orelse,
.?) invece di essere una
libreria o un tipo speciale da manipolare con metodi. Questo permette di
scrivere codice molto conciso, ma con la sicurezza di un controllo esplicito
dell'assenza di valore.
Una particolarità è il cosiddetto unwrap forzato che genera un panic se il valore è null. Da usare solo in casi partciolari e se si è sicuri che il valore sia diverso da null, in pratica qualcuno dice che serve da allarme per situazioni imprevedibili:
const a: ?i32 = null;
const b = a orelse 42; // b = 42
Chiudiamo con una tabella riassuntiva che richiama anche l'argomento dei puntatori:
| Concetto | Significato | Esempio |
|---|---|---|
| Optional |
Valore T o null
|
?i32
|
| Unwrap forzato | Panic se null |
x.?
|
| Default | Sostituisce null |
x orelse 0
|
| Optional pointer | Puntatore può essere null |
?*T
|
| Pointer to optional | Puntatore valido, contenuto opzionale |
*?T
|