L'uso di un linguaggio più rigoroso non si limita a spostare i post degli obiettivi dall'implementazione corretta all'ottenimento delle specifiche giuste. È difficile fare qualcosa che sia molto sbagliato ma logicamente coerente; ecco perché i compilatori catturano così tanti bug.
L'aritmetica del puntatore, come è normalmente formulata, non è corretta perché il sistema dei tipi in realtà non significa ciò che dovrebbe significare. Puoi evitare completamente questo problema lavorando in un linguaggio di raccolta dei rifiuti (l'approccio normale che ti fa anche pagare per l'astrazione). Oppure puoi essere molto più specifico su quali tipi di puntatori stai utilizzando, in modo che il compilatore possa rifiutare tutto ciò che è incoerente o semplicemente non può essere dimostrato corretto come scritto. Questo è l'approccio di alcune lingue come Rust.
I tipi costruiti sono equivalenti alle prove, quindi se scrivi un sistema di tipi che lo dimentica, tutti i tipi di cose vanno male. Supponiamo per un po 'che quando dichiariamo un tipo, in realtà intendiamo che stiamo affermando la verità su ciò che è nella variabile.
- int * x; // Una falsa affermazione. x esiste e non punta a un int
- int * y = z; // Vero solo se è dimostrato che z punta a un int
- * (x + 3) = 5; // Vero solo se (x + 3) punta a un int nella stessa matrice di x
- int c = a / b; // Vero solo se b è diverso da zero, come: "nonzero int b = ...;"
- nullable int * z = NULL; // nullable int * non è uguale a un int *
- int d = * z; // Una falsa affermazione, perché z è nullable
- if (z! = NULL) {int * e = z; } // Ok perché z non è null
- libera (y); int w = * y; // Falsa affermazione, perché y non esiste più in w
In questo mondo, i puntatori non possono essere nulli. Le dereferenze NullPointer non esistono e non è necessario verificare la nullità dei puntatori da nessuna parte. Invece, un "nullable int *" è un tipo diverso che può avere il suo valore estratto su null o su un puntatore. Ciò significa che nel punto in cui inizia l' assunzione non nulla , vai a registrare la tua eccezione o vai in giù un ramo null.
In questo mondo, non esistono nemmeno errori fuori campo. Se il compilatore non è in grado di provare che è nei limiti, prova a riscriverlo in modo che il compilatore possa provarlo. Se ciò non è possibile, dovrai inserire l'Assunta manualmente in quel punto; il compilatore potrebbe trovare una contraddizione in seguito.
Inoltre, se non è possibile avere un puntatore che non è inizializzato, non si avranno puntatori alla memoria non inizializzata. Se hai un puntatore alla memoria liberata, allora dovrebbe essere rifiutato dal compilatore. In Rust, ci sono diversi tipi di puntatori per rendere ragionevole aspettarsi questo tipo di prove. Esistono solo puntatori di proprietà (ovvero: nessun alias), puntatori a strutture profondamente immutabili. Il tipo di archiviazione predefinito è immutabile, ecc.
Esiste anche il problema di applicare una grammatica effettiva ben definita sui protocolli (che include i membri dell'interfaccia), per limitare l'area di input a esattamente ciò che è previsto. La cosa sulla "correttezza" è: 1) Sbarazzarsi di tutti gli stati indefiniti 2) Garantire la coerenza logica . La difficoltà di arrivarci ha molto a che fare con l'uso di utensili estremamente cattivi (dal punto di vista della correttezza).
Questo è esattamente il motivo per cui le due peggiori pratiche sono variabili globali e goto. Queste cose impediscono di porre condizioni pre / post / invarianti attorno a qualsiasi cosa. È anche il motivo per cui i tipi sono così efficaci. Man mano che i tipi diventano più forti (usando infine i Tipi dipendenti per tenere conto del valore reale), si avvicinano a essere prove costruttive di correttezza in se stessi; la compilazione di programmi incoerenti non riesce.
Tieni presente che non si tratta solo di errori stupidi. Si tratta anche di difendere la base di codice da infiltrati intelligenti. Ci saranno casi in cui devi rifiutare un invio senza una convincente prova generata dalla macchina di proprietà importanti come "segue il protocollo formalmente specificato".