Rénovo X240

J'ai récupéré un ordinateur portable Lenovo ThinkPad X240 pour lui donner une seconde jeunesse1. Au départ, il s'agissait simplement de changer le clavier qui était défectueux depuis qu'il avait été aspergé d'une tasse de café. Et puis la loi de Murphy a frappé une deuxième fois cette machine.

La gamme des ordinateurs ThinkPad est très répandue en entreprise ce qui explique qu'on trouve aisément des pièces de rechange. Après avoir suivi les instructions de cet excellent tutoriel vidéo pour changer le clavier, je démarrais l'ordinateur déjà fier de ma réparation. Aussitôt, j'entendis un léger claquement – Plop – accompagné par l'odeur âcre d'un composant électronique qui venait de brûler. Le message d'erreur suivant s'affichait à l'écran : Fan error. Ça ne sentait pas bon…

Effectivement, le ventilateur ne tournait plus ce qui interrompait la séquence de démarrage. J'essayais alors une astuce trouvée sur Internet : lors du démarrage, je soufflais fortement à travers la sortie d'aération du ventilateur pour le mettre en mouvement et faire croire au BIOS que le ventilateur fonctionnait. L'ordinateur démarrait bien mais le ventilateur demeurait immobile sous Linux ; je décidais sagement d'éteindre la machine pour éviter que son processeur ne surchauffe.

Le lendemain, je démontais le ventilateur pour le tester séparément. Ne disposant pas d'une source de courant continu de 5 volts, j'en bricolais une en coupant un câble USB. En effet, entre le fil rouge et le fil noir, la tension est de 5 volts. Ainsi alimenté, le ventilateur brassait l'air à plein régime.

En observant avec plus d'attention la carte mère, je remarquais une tâche noire à côté du connecteur du ventilateur. Je tenais le coupable : un composant électronique avait bel et bien grillé.

Fusible grillé en amont du ventilateur

À ce stade, je ne connaissais ni la nature ni les caractéristiques du composant qui avait grillé. Heureusement, un collègue trouvait sur ce forum les schémas électroniques du Lenovo X2402. À la page 50 du document, se trouvait la réponse à ma question : le défunt composant était un fusible qui pouvait supporter un courant d'une intensité de 2 ampères. Il a eu très chaud !

Schéma du connecteur du ventilateur

J'ai la chance de travailler dans l'électronique grand public et d'avoir des collègues électroniciens qui savent souder des composants montés en surface. Ils sont très adroits parce que les composants montés en surface sont minuscules (à titre d'exemple le fusible mentionné mesure 1 mm × 0.5 mm). Il n'aura fallu que cinq minutes à l'un de mes collègues pour remplacer le regretté fusible par un fil, faute de disposer d'un remplaçant3. Voici le résultat :

Remplacement du fusible grillé par un fil

Et le patient dans tout ça ? Il se porte bien puisque c'est depuis le Rénovo X240 que j'écris ces lignes.


  1. Merci Guillaume. 

  2. Merci Rémi. 

  3. Merci Jean-Luc. 

Rusticity: convert an integer to an enum

I am learning how to program in Rust. Thinking in Rust is a delightful experience and the more I practice Rust the more I feel how it empowers developers to solve complex problems with confidence.

However, I sometimes get frustrated on my way to rusticity. For instance, when a programming task easily done in C or Python requires more work in Rust. I experienced this when trying to convert an integer to an enum. This blog post compares how this is usually done in C and how to do it safely in Rust.

Converting an integer to an enum in C

In C, the enumeration constants have the type int. Thus, an integer value can be directly assigned to an enum.

#include <stdio.h>

enum atomic_number {
    HYDROGEN = 1,
    HELIUM = 2,
    // ...
    IRON = 26,
};

int main(void)
{
    enum atomic_number element = 26;

    if (element == IRON) {
        printf("Beware of Rust!\n");
    }

    return 0;
}

While it is easy to assign an integer value to an enum, the C compiler performs no bounds checking. Nothing prevents us from assigning an impossible value to an atomic_number enum.

Converting an integer to an enum in Rust, the naïve way

Let's write a similar program in Rust:

enum AtomicNumber {
    HYDROGEN = 1,
    HELIUM = 2,
    // ...
    IRON = 26,
}

fn main() {
    let element: AtomicNumber = 26;
}

When we try to compile and run the program with cargo run, the Rust compiler reports a mismatched types error:

error[E0308]: mismatched types
 --> src/main.rs:9:33
  |
9 |     let element: AtomicNumber = 26;
  |                  ------------   ^^ expected enum `AtomicNumber`, found integer
  |                  |
  |                  expected due to this

The compiler error clearly indicates that AtomicNumber and integer are two different types.

To explicitly convert an integer to our AtomicNumber enum, we can write a conversion function that takes an unsigned 32-bits integer as parameter and returns an AtomicNumber.

enum AtomicNumber {
    HYDROGEN = 1,
    HELIUM = 2,
    // ...
    IRON = 26,
}

impl AtomicNumber {
    fn from_u32(value: u32) -> AtomicNumber {
        match value {
            1 => AtomicNumber::HYDROGEN,
            2 => AtomicNumber::HELIUM,
            // ...
            26 => AtomicNumber::IRON,
            _ => panic!("Unknown value: {}", value),
        }
    }
}

fn main() {
    let element = AtomicNumber::from_u32(26);
}

The from_u32() function is an associated function of the AtomicNumber type because it is associated with that type, and unlike a method does not take a first parameter named self.

There are several issues in from_u32():

  • When the given value does not match any variant in the enumeration, panic!() terminates the program.
  • Both the enumeration definition and the conversion function match enumeration variants with integers. This duplication is error-prone.
  • The conversion function grows with the number of variants.

The periodic table contains more than 100 elements. Implementing a conversion function for atomic numbers is boring and error-prone. There is a better way.

Converting an integer to an enum in Rust with num-derive

A more elegant solution is to use the FromPrimitive trait from the num crate coupled with syntax extensions from the num-derive crate.

In Cargo.toml, add dependencies for num, num-derive, and num-traits:

[dependencies]
num = "0.4"
num-derive = "0.4"
num-traits = "0.2"

Then, use the #[derive] attribute:

extern crate num;
#[macro_use]
extern crate num_derive;

#[derive(FromPrimitive)]
enum AtomicNumber {
    HYDROGEN = 1,
    HELIUM = 2,
    // ...
    IRON = 26,
}

fn main() {
    let element = num::FromPrimitive::from_u32(26);
    match element {
        Some(AtomicNumber::IRON) => println!("Beware of Rust!"),
        Some(_) => {},
        None => println!("Unknown atomic number")
    }
}

The #[derive(FromPrimitive)] attribute instructs the Rust compiler to generate a basic implementation of the FromPrimitive trait for the AtomicNumber enumeration. Unlike our handwritten from_u32() function, the conversion function generated by the Rust compiler returns an Option which is either Some atomic number or None if the given integer does not match any known atomic number. This is much safer than calling panic!().

With the #[derive(FromPrimitive)] attribute our Rust program is nearly as concise as the same program written in C with the bonus of being safer.

Harder, better, safer, rustier

While I had some hard time figuring how to convert an integer value to an enum variant in Rust, I feel reassured by its type safety and pleased by its ecosystem of crates.

Rust on the pont de Bir-Hakeim

Rust on the pont de Bir-Hakeim