160 lines
2.9 KiB
Markdown
160 lines
2.9 KiB
Markdown
# Exemples / DO-DON'T — Chapitre 18 — `Result`, erreurs, exceptions et faults
|
|
|
|
> Document compagnon. Les exemples illustrent les règles du chapitre ; la formulation normative reste dans le chapitre lui-même.
|
|
|
|
## Hiérarchie
|
|
|
|
```text
|
|
Object
|
|
└── Error
|
|
├── ResultError
|
|
├── Exception
|
|
└── Fault
|
|
```
|
|
|
|
```text
|
|
public final class ParseError extends ResultError {
|
|
}
|
|
|
|
public final class FileNotFoundException extends Exception {
|
|
}
|
|
|
|
public final class IndexOutOfBoundsFault extends Fault {
|
|
}
|
|
```
|
|
|
|
## `ResultError`
|
|
|
|
DO : utiliser `Result` lorsque l'échec est une valeur normale à inspecter explicitement.
|
|
|
|
```text
|
|
func parseExternalInput(String input) -> Result<Value, ParseError> {
|
|
...
|
|
}
|
|
```
|
|
|
|
DON'T : placer une `Exception` ou un `Fault` dans le paramètre erreur de `Result`.
|
|
|
|
```text
|
|
Result<Value,FileNotFoundException> // ERROR
|
|
Result<Value,IndexOutOfBoundsFault> // ERROR
|
|
```
|
|
|
|
## `Exception`, `throw` et `throws`
|
|
|
|
```text
|
|
func readConfig(String path) -> Config
|
|
throws IOException
|
|
{
|
|
...
|
|
}
|
|
```
|
|
|
|
Un retour direct est compatible avec `throws`.
|
|
|
|
```text
|
|
throw FileNotFoundException(...); // OK
|
|
throw IndexOutOfBoundsFault(...); // ERROR
|
|
throw ParseError(...); // ERROR
|
|
```
|
|
|
|
## `Fault`, `fault` et `faults`
|
|
|
|
```text
|
|
method elementAt(uint64 index) -> T
|
|
faults IndexOutOfBoundsFault
|
|
{
|
|
if (index >= this::length()) {
|
|
fault IndexOutOfBoundsFault(index, this::length());
|
|
}
|
|
|
|
...
|
|
}
|
|
```
|
|
|
|
La clause `faults` est optionnelle et non exhaustive.
|
|
|
|
DO : appeler sans cérémonie lorsque le fault n'est pas un chemin normal à traiter.
|
|
|
|
```text
|
|
T value = list::elementAt(index);
|
|
```
|
|
|
|
DO : capturer explicitement lorsque le programme veut réellement récupérer ce cas.
|
|
|
|
```text
|
|
try {
|
|
T value = list::elementAt(index);
|
|
} catch (IndexOutOfBoundsFault faultValue) {
|
|
...
|
|
}
|
|
```
|
|
|
|
Aucune propagation de `faults` n'est obligatoire :
|
|
|
|
```text
|
|
func outer() -> Void {
|
|
inner(); // inner peut déclarer faults SomeFault
|
|
return Void;
|
|
}
|
|
```
|
|
|
|
## `catch`
|
|
|
|
Valide :
|
|
|
|
```text
|
|
catch (IOException error) {
|
|
...
|
|
}
|
|
|
|
catch (IteratorInvalidatedFault faultValue) {
|
|
...
|
|
}
|
|
```
|
|
|
|
Invalide :
|
|
|
|
```text
|
|
catch (Error error) // ERROR
|
|
catch (ResultError error) // ERROR
|
|
```
|
|
|
|
`catch` ne peut cibler que `Exception`, `Fault` ou leurs descendants.
|
|
|
|
## Direct versus valeur conditionnelle
|
|
|
|
Accès affirmatif :
|
|
|
|
```text
|
|
User user = users[id]; // absence -> KeyNotFoundFault
|
|
```
|
|
|
|
Absence normale :
|
|
|
|
```text
|
|
Option<User> user = users::get(id);
|
|
```
|
|
|
|
Le Core ne doit pas créer automatiquement une variante `tryOp()` uniquement pour transporter le même échec dans `Result`.
|
|
|
|
## Erreur statique versus fault runtime
|
|
|
|
```text
|
|
StaticArray<int32,3> values = [1, 2, 3];
|
|
int32 a = values[5]; // ERROR compilation
|
|
```
|
|
|
|
```text
|
|
uint64 index = readIndex();
|
|
int32 b = values[index]; // peut produire IndexOutOfBoundsFault au runtime
|
|
```
|
|
|
|
Un statement `fault` explicite reste évidemment valide :
|
|
|
|
```text
|
|
if (!state::isValid()) {
|
|
fault InvalidStateFault(...);
|
|
}
|
|
```
|