Cours de programmation séquentielle

Gestion d’erreurs

Orestis Malaspinas

Gestion d’erreurs

  • Il est important de pouvoir gérer trois cas:
    • La panique totale (erreur fatale du programme).
    • L’erreur dont on peut se remettre.
    • Un résultat qui peut contenir une valeur ou pas.

Panique!

panic!()

  • Nous avons vu le macro panic!("texte").
  • Le programme s’arrête et affiche le "texte".
  • Utilisation:
    • Le programme ne doit pas continuer quoi qu’il arrive.
      
        fn elem(v: &[i32], i: usize) -> i32 {
            if i >= v.len() {
                panic!("Erreur fatale!");
            }
            // assert!(i < v.len(), "Erreur fatale!"); // ceci est équivalent
            v[i]
        }
      
        fn main() {
            let v = [1, 2, 3, 4];
            elem(&v, 100);
        }
        
    • Avec l’appel RUST_BACKTRACE=1 on peut remonter la pile d’appels (debugging).

          $ RUST_BACKTRACE=1 cargo run
          Finished dev [unoptimized + debuginfo] target(s) in 0.01s                                                                                                                                                      
           Running `target/debug/ma_librairie`
      thread 'main' panicked at 'Erreur fatale!', src/main.rs:5:3
      stack backtrace:
         0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
                   at libstd/sys/unix/backtrace/tracing/gcc_s.rs:49
         1: std::sys_common::backtrace::print
                   at libstd/sys_common/backtrace.rs:71
                   at libstd/sys_common/backtrace.rs:59
         2: std::panicking::default_hook::{{closure}}
                   at libstd/panicking.rs:211
         3: std::panicking::default_hook
                   at libstd/panicking.rs:227
         4: std::panicking::rust_panic_with_hook
                   at libstd/panicking.rs:477
         5: std::panicking::begin_panic
                   at /checkout/src/libstd/panicking.rs:411
         6: ma_librairie::elem
                   at src/main.rs:5
         7: ma_librairie::main
                   at src/main.rs:12
         8: std::rt::lang_start::{{closure}}
                   at /checkout/src/libstd/rt.rs:74
         9: std::panicking::try::do_call
                   at libstd/rt.rs:59
                   at libstd/panicking.rs:310
        10: __rust_maybe_catch_panic
                   at libpanic_unwind/lib.rs:102
        11: std::rt::lang_start_internal
                   at libstd/panicking.rs:289
                   at libstd/panic.rs:392
                   at libstd/rt.rs:58
        12: std::rt::lang_start
                   at /checkout/src/libstd/rt.rs:74
        13: main
        14: __libc_start_main
        15: _start

Asserts

  • On peut utiliser trois macros forts utiles.
  • assert!(cond, "Texte");

fn main() {
    let num = 1;
    let denum = 0;

    assert!(denum != 0, "Le dénominateur doit être non nul.");
    let _total = num / denum;
}
  • assert_eq!(lhs, rhs, "Texte");

fn main() {
    let a = 1;
    let b = 0;

    assert_eq!(a, b, "A et b devraient être égales.");
}
  • assert_ne!(lhs, rhs, "Texte");

fn main() {
    let a = 1;
    let b = 0;

    assert_ne!(a, b, "A et b devraient être différentes.");
}

Options

  • Un type énuméré particulièrement utile est le type Option

    enum Option<T> { // <T> est une notation pour un type générique
        Some(T),
        None,
    }
  • Type utilisé lorsque une valeur peut être “quelque chose” ou “rien” (ex: recherche d’un élément dans un tableau).
  • Exmple: la division

    
      fn main() {
          let num = 1;
          let denum = 4;
    
          let div = 
              if denum == 0 {
                  None
              } else {
                  Some(num / denum)
              };
    
          match div {
              Some(d) => println!("{} divisé par {} donne {}", num, denum, d),
              None => println!("Cette division n'existe pas."),
          }
      }
      
  • Il est commun que des fonction retournent des Option.

Résultat

Lorsqu’on peut commettre des erreurs non-fatales

  • Pour un traitement plus fin des erreurs l’enum

    
      enum Result<T, E> {
         Ok(T),
         Err(E),
      }
      
  • Contient le un élément de type T quand tout se passe bien et E pour l’erreur.

    
          fn elem(v: &[i32], i: usize) -> Result<i32, &str> {
            if i >= v.len() {
                return Err("L'index est trop grand!")
            } else {
                Ok(v[i])
            }
          }
    
          fn main() {
            let v = [1, 2, 3, 4];
            match elem(&v, 100) {
                Ok(i) => println!("L'élément est {}", i),
                // Err(error) => panic!("The problem is {:?}", error),
                Err(_) => println!("Mince ça a raté."),
            }
          }