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.
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.