modulo 02 / lezione 12
Leggere errori e stack trace
Errori cross-piattaforma
Riconoscere lo stesso schema sotto messaggi diversi: NullPointerException, nil, KeyError, Segmentation fault, panic.
In parole semplici
Linguaggi diversi danno nomi diversi allo stesso problema. Il classico “ho usato qualcosa che non esisteva” si chiama TypeError in JavaScript, NullPointerException in Java, nil in Swift, KeyError o TypeError in Python, Segmentation fault in C, panic in Go o Rust. Riconoscere lo schema sotto la facciata ti permette di portare l’esperienza di un linguaggio all’altro.
Parole nuove
- null/nil/None: rappresentazione di “valore assente” in vari linguaggi.
- null pointer: puntatore non inizializzato o impostato a “nessun indirizzo”.
- panic: errore non recuperabile in Go o Rust.
- segmentation fault (segfault): accesso a memoria non valida in C/C++.
Cosa impari
- Mappare lo stesso bug logico nei messaggi di cinque linguaggi.
- Sapere che dietro a nomi diversi spesso vive lo stesso schema.
- Trasferire intuizioni di debug da un linguaggio noto a uno nuovo.
Lo stesso bug in cinque linguaggi
Bug: chiamare un metodo su un valore “vuoto”.
JavaScript
const u = undefined;
u.nome;
// TypeError: Cannot read properties of undefined (reading 'nome')
Python
u = None
u.nome
# AttributeError: 'NoneType' object has no attribute 'nome'
d = {"a": 1}
d["b"]
# KeyError: 'b'
Java / Kotlin
String s = null;
s.length();
// java.lang.NullPointerException
Swift
let u: User? = nil
u!.nome
// Fatal error: Unexpectedly found nil while unwrapping an Optional value
C / C++
int *p = NULL;
*p = 5;
// Segmentation fault (core dumped)
Go
var u *User
fmt.Println(u.Nome)
// runtime error: invalid memory address or nil pointer dereference
Rust
let v: Option<&str> = None;
v.unwrap();
// thread 'main' panicked at 'called `Option::unwrap()` on a `None` value'
Tutti, in lingue diverse, dicono la stessa cosa: “ho usato qualcosa che non c’era”.
Schemi ricorrenti
| Schema | JS | Python | Java | Swift | Go | Rust |
|---|---|---|---|---|---|---|
| valore assente | undefined/null | None | null | nil | nil | None |
| accesso a chiave inesistente | undefined | KeyError | NullPointerException* | nil | “” o panic | None |
| array fuori bounds | undefined | IndexError | ArrayIndexOutOfBoundsException | crash | panic | panic |
| tipo sbagliato | TypeError | TypeError | ClassCastException | type mismatch | type mismatch | type mismatch |
| divisione per zero | Infinity/NaN | ZeroDivisionError | ArithmeticException | crash | panic | panic |
* Java distingue tra null e “chiave non presente” se usi Map.get che ritorna null.
Perché conta
Se hai imparato a debuggare un TypeError in JavaScript, sai già:
- guardare lo stack;
- chiedere “da dove arriva questo valore?”;
- risalire la catena.
Quando incontri un NullPointerException o un panic Go la prima volta, non ricominci da zero: applichi lo stesso schema.
Esempio: lo stesso bug logico portato altrove
Lo stesso codice JS:
function media(numeri) {
return numeri.reduce((a, n) => a + n, 0) / numeri.length;
}
media();
Errore JavaScript:
TypeError: Cannot read properties of undefined (reading 'reduce')
In Python equivalente:
def media(numeri):
return sum(numeri) / len(numeri)
media()
Errore Python:
TypeError: media() missing 1 required positional argument: 'numeri'
In Java equivalente:
static double media(int[] numeri) {
int somma = 0;
for (int n : numeri) somma += n;
return (double) somma / numeri.length;
}
// invocata con null:
media(null);
// java.lang.NullPointerException
Tre messaggi diversi. Stesso schema: “il parametro che richiede esistere non c’era”.
Lab cross-piattaforma
Scegli un piccolo bug logico (es. divisione per zero, accesso a indice inesistente, valore opzionale non gestito).
- Riproducilo in tre linguaggi che conosci anche solo di vista (JS, Python, e uno a scelta).
- Cattura il messaggio di errore esatto.
- Compila una riga della tua tabella personale schema → linguaggio → messaggio.
Quando l’analogia non regge
Alcuni schemi non hanno corrispondente perfetto:
- C segfault spesso non dice nemmeno dove. Senza debugger (gdb, lldb) è cieco.
- Rust Option/Result sposta l’errore a compile-time: il bug “uso un valore assente” diventa impossibile se non lo gestisci esplicitamente.
- JavaScript silenzioso:
arr[100]su un array da 3 elementi ritornaundefinedsenza errore. Lo scopri solo dopo.
Quando l’analogia non regge, è ancora più importante leggere il runtime model del linguaggio nuovo.
Esercizi
Base · facile
Questi tre messaggi vengono da linguaggi diversi ma descrivono lo stesso schema. Quale?
1) TypeError: Cannot read properties of undefined (reading 'nome') (JavaScript)
2) java.lang.NullPointerException (Java)
3) Fatal error: Unexpectedly found nil while unwrapping an Optional (Swift)
Soluzione
Schema: uso di un valore assente (null / nil / undefined) come se contenesse qualcosa. In tutti e tre i casi si è acceduto a una proprietà o a un metodo su un valore “vuoto”. Cambia il nome (TypeError, NullPointerException, unwrap di nil), non il problema.
Intermedio · medio
Per ognuno di questi errori indica il linguaggio probabile e a quale schema appartiene (valore assente, valore fuori range/chiave mancante, memoria non valida).
1) KeyError: 'b'
2) Segmentation fault (core dumped)
3) panic: runtime error: index out of range [3] with length 3
Soluzione
- Python — chiave inesistente in un dizionario: schema valore/chiave mancante (parente di “assente”, ma su una collezione).
- C/C++ — accesso a memoria non valida (es. dereferenziare
NULL): schema memoria non valida. È la versione “bassa” del valore assente. - Go — indice oltre la fine di uno slice: schema valore fuori range.
Tre famiglie distinte: chi conosce “index out of range” in un linguaggio riconosce subito il RangeError/eccezione equivalente in un altro.
Sfida · difficile
Prendi un bug logico che conosci in un linguaggio (es. accesso a un campo di un oggetto che potrebbe essere assente) e scrivi come si manifesterebbe in tre linguaggi diversi, con il messaggio tipico di ciascuno. Spiega perché riconoscere lo schema, e non il nome, rende trasferibile l’esperienza di debug.
Soluzione
Bug: leggere utente.nome quando utente può essere assente.
// JavaScript
utente.nome; // TypeError: Cannot read properties of undefined (reading 'nome')
# Python
utente.nome # AttributeError: 'NoneType' object has no attribute 'nome'
// Swift
utente!.nome // Fatal error: Unexpectedly found nil while unwrapping an Optional value
È sempre lo stesso schema: “ho usato qualcosa che poteva non esserci senza prima verificarlo”. Se lo riconosci, in un linguaggio nuovo non parti da zero: cerchi subito dove nasce il valore assente e perché non è stato controllato, invece di farti spaventare da un messaggio sconosciuto. Il nome cambia tra piattaforme; la domanda diagnostica no.
Errori frequenti del principiante
- Pensare che il linguaggio nuovo sia “strano”. Il messaggio cambia, il problema no.
- Aspettarsi un errore quando il linguaggio è silenzioso. JS non protesta su array out-of-bounds.
- Confidare nel debugger del linguaggio originale. gdb non aiuta su una JVM, jdb non aiuta su Python.
- Trattare ogni linguaggio come unico. Gli schemi sono pochi, le facce molte.