IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Rust par l'exemple


précédentsommairesuivant

5. Le casting

Rust ne permet pas la conversion implicite des types primitifs (coercition). En revanche, une conversion explicite (casting) peut être entreprise à l'aide du mot-clé as.

Les règles régissant la conversion entre les types littéraux s'inspirent, principalement, des conventions du langage C à l'exception des cas où le C réserve des comportements imprévisibles.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
// Supprime tous les avertissements relatifs aux dépassements 
// de capacité (e.g. une variable de type u8 ne peut pas 
// contenir plus qu'une variable de type u16).
#![allow(overflowing_literals)]

fn main() {
    let decimal = 65.4321_f32;

    // Erreur! La conversion implicite n'est pas supportée.
    // let integer: u8 = decimal;
    // FIXME ^ Décommentez/Commentez cette ligne pour voir 
    // le message d'erreur apparaître/disparaître.

    // Conversion explicite.
    let integer = decimal as u8;
    let character = integer as char;

    println!("Casting: {} -> {} -> {}", decimal, integer, character);

    // Lorsque vous convertissez une valeur vers un type 
    // non-signé T, std::T::MAX + 1 est incrémenté ou soustrait jusqu'à 
    // ce que la valeur respecte la capacité du nouveau type.

    // 1000 ne dépasse pas la capacité d'un entier non-signé codé sur 16 bits.
    println!("1000 as a u16 is: {}", 1000 as u16);

    // 1000 - 256 - 256 - 256 = 232
    // En réalité, les 8 premiers bits les plus faibles (LSB) sont conservés et les 
    // bits les plus forts (MSB) restants sont tronqués.
    println!("1000 as a u8 is : {}", 1000 as u8);
    // -1 + 256 = 255
    println!("  -1 as a u8 is : {}", (-1i8) as u8);

    // Pour les nombres positifs, cette soustraction est équivalente à une 
    // division par 256.
    println!("1000 mod 256 is : {}", 1000 % 256);

    // Quand vous convertissez un type d'entiers signés, le résultat (bit à bit)
    // est équivalent à celui de la conversion vers un type d'entiers non-signés.
    // Si le bit de poids fort vaut 1, la valeur sera négative.

    // Sauf si il n'y a pas de dépassements, évidemment.
    println!(" 128 as a i16 is: {}", 128 as i16);
    // 128 as u8 -> 128, complément à deux de 128 codé sur 8 bits:
    println!(" 128 as a i8 is : {}", 128 as i8);

    // On répète l'exemple ci-dessus.
    // 1000 as u8 -> 232
    println!("1000 as a i8 is : {}", 1000 as i8);
    // et le complément à deux de 232 est -24.
    println!(" 232 as a i8 is : {}", 232 as i8);


}

5-1. Les littéraux

Les littéraux numériques peuvent être typés en suffixant le littéral avec son type. Par exemple, pour préciser que le littéral 42 devrait posséder le type i32, nous écrirons 42i32.

Le type des littéraux numériques qui ne sont pas suffixés va dépendre du contexte dans lequel ils sont utilisés. S'il n'y a aucune contrainte (i.e. si rien ne force la valeur à être codée sur un nombre de bits bien précis), le compilateur utilisera le type i32 pour les entiers et f64 pour les nombres réels.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
fn main() {
    // Ces littéraux sont suffixés, leurs types sont connus à l'initialisation.
    let x = 1u8;
    let y = 2u32;
    let z = 3f32;

    // Ces littéraux ne sont pas suffixés, leurs types dépendent du contexte.
    let i = 1;
    let f = 1.0;

    // La fonction `size_of_val` renvoie la taille d'une variable en octets.
    println!("La taille de `x` en octets: {}", std::mem::size_of_val(&x));
    println!("La taille de `y` en octets: {}", std::mem::size_of_val(&y));
    println!("La taille de `z` en octets: {}", std::mem::size_of_val(&z));
    println!("La taille de `i` en octets: {}", std::mem::size_of_val(&i));
    println!("La taille de `f` en octets: {}", std::mem::size_of_val(&f));
}

Certains concepts présentés dans l'exemple ci-dessus n'ont pas encore été abordés. Pour les plus impatients, voici une courte explication :

  • fun(&foo) : Cette syntaxe représente le passage d'un paramètre par référence plutôt que par valeur (i.e. fun(foo)). Pour plus d'informations, voir le chapitre du système d'emprunts.
  • std::mem::size_of_val est une fonction mais appelée avec son chemin absolu. Le code peut être divisé et organisé en plusieurs briques logiques nommées modules. Pour le cas de la fonction size_of_val, elle se trouve dans le module mem, lui-même se trouvant dans le paquet std. Pour plus d'informations voir les modules et/ou les « crates ».

5-2. L'inférence des types

Le moteur dédié à l'inférence des types est très intelligent. Il fait bien plus que d'inférer le type d'une r-value à l'initialisation. Il se charge également d'analyser l'utilisation de la variable dans la suite du programme pour inférer son type définitif. Voici un exemple plus avancé dédié à l'inférence :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
fn main() {
    // Dû à l'annotation(suffixe), le compilateur sait que `elem` possède le type 
    // u8.
    let elem = 5u8;

    // Crée un vecteur vide (un tableau dont la taille n'est pas définie).
    let mut vec = Vec::new();
    // À ce niveau, le compilateur ne connaît pas encore le type exact de `vec`,
    // il sait simplement que c'est un vecteur de quelque chose (`Vec<_>`).

    // On ajoute `elem` dans le vecteur.
    vec.push(elem);
    // Tada! Maintenant le compilateur sait que `vec` est un vecteur 
    // d'entiers non-signés typés `u8` (`Vec<u8>`).
    // TODO ^ Essayez de commenter la ligne où se trouve `vec.push(elem)`.

    println!("{:?}", vec);
}

Aucun typage explicite n'était nécessaire, le compilateur est heureux et le programmeur aussi !

5-3. Les alias

Le mot-clé type peut être utilisé pour donner un nouveau nom à un type existant. Les types doivent respecter la convention de nommage CamelCase ou le compilateur vous renverra un avertissement. L'exception à cette règle sont les types primitifs : usize, f32, etc.

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
// `NanoSecond` est le nouveau nom de `u64`.
type NanoSecond = u64;
type Inch = u64;

// Utilisons un attribut pour faire taire les 
// avertissements.
#[allow(non_camel_case_types)]
type u64_t = u64;
// TODO ^ Essayez de supprimer l'attribut.

fn main() {
    // `NanoSecond` = `Inch` = `u64_t` = `u64`.
    let nanoseconds: NanoSecond = 5 as u64_t;
    let inches: Inch = 2 as u64_t;
    
    // Notez que les alias de types ne fournissent aucune sécurité supplémentaire,
    // car ce ne sont pas de nouveaux types (i.e. vous pouvez très bien changer
    // Inch par NanoSecond, vous n'aurez aucune erreur).
    println!("{} nanoseconds + {} inches = {} unit?",
             nanoseconds,
             inches,
             nanoseconds + inches);
}

Voir aussi

Les attributs.


précédentsommairesuivant

Licence Creative Commons
Le contenu de cet article est rédigé par Rust Core Team et est mis à disposition selon les termes de la Licence Creative Commons Attribution 3.0 non transposé.
Les logos Developpez.com, en-tête, pied de page, css, et look & feel de l'article sont Copyright © 2013 Developpez.com.