English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Gestion de l'organisation Rust

Any programming language that cannot organize code is difficult to delve into, almost no software product is compiled from a single source file.

Jusqu'à présent, tous les programmes de ce tutoriel ont été écrits dans un seul fichier, principalement pour faciliter l'apprentissage de la syntaxe et des concepts du langage Rust.

Pour un projet, l'organisation du code est extrêmement importante.

Il y a trois concepts d'organisation importants en Rust : coffre, paquet, module.

Coffre(Crate)

"Coffre" est un fichier de programme binaire ou un fichier de bibliothèque, existant dans "paquet".

"Coffre" est une structure en arbre, et le sommet de cet arbre est le programme compilé des fichiers sources compilés par le compilateur au début de son exécution.

Remarque : "fichier de programme binaire" n'est pas nécessairement "fichier exécutable binaire", il ne peut être déterminé que comme un fichier contenant le langage machine de l'ordinateur cible, le format du fichier variant selon l'environnement de compilation.

Paquet(Package)

Lorsque nous utilisons la commande new de Cargo pour créer un projet Rust, un fichier Cargo.toml est créé dans le répertoire du projet. La substance du projet est un paquet, le paquet doit être géré par un fichier Cargo.toml, qui décrit les informations de base du paquet et les dépendances.

Un paquet peut contenir au plus un coffre-fort de bibliothèque, peut contenir n'importe quel nombre de coffres-forts binaires, mais doit au moins contenir un coffre-fort (soit une bibliothèque ou un coffre-fort binaire).

Lorsque vous créez un paquet avec la commande cargo new, un fichier source main.rs est généré dans le répertoire src, Cargo considère ce fichier comme la racine du coffre-fort binaire, et le binaire compilé sera le même que le nom du paquet.

Module(Module)

Pour un travail de génie logiciel, nous organisons souvent selon les normes d'organisation des langages de programmation utilisés, la structure principale des modules est souvent en arbre. L'unité principale des modules organisationnels en Java est la classe, tandis que la principale méthode d'organisation des modules en JavaScript est la fonction.

Les unités d'organisation avancées de ces langages peuvent être imbriquées de manière hiérarchique, comme la structure des répertoires du système de fichiers. L'unité d'organisation dans Rust est le module (Module).

mod nation {
    mod government {
        fn govern() {}
    }
    mod congress {
        fn legislate() {}
    }
    mod court {
        fn judicial() {}
    }
}

Voici une description procédurale d'un État de droit : l'État (nation) inclut le gouvernement (government), le congrès (congress) et la cour (court), chacun ayant des fonctions administratives, législatives et judiciaires. Nous pouvons le convertir en une structure en arbre :

nation
 ├── government
 │ └── govern
 ├── congress
 │ └── legislate
 └── court
   └── judicial

Dans le système de fichiers, la structure des répertoires est généralement représentée par les barres obliques dans les chaînes de chemins, le séparateur de chemins dans Rust est ::.

Les chemins sont divisés en chemins absolus et chemins relatifs. Le chemin absolu commence par le mot-clé crate. Le chemin relatif commence par les mots-clés self ou super ou un identificateur. Par exemple :

crate::nation::government::govern();

C'est le chemin absolu de la fonction govern, et le chemin relatif peut être représenté ainsi :

nation::government::govern();

Vous pouvez maintenant essayer de définir une structure de module similaire dans un programme source et d'utiliser le chemin dans la fonction principale.

Si vous faites ainsi, vous trouverez certainement des endroits incorrects : le module government et ses fonctions sont privés, vous n'êtes pas autorisé à les accéder.

Droits d'accès

Dans Rust, il y a deux types simples de droits d'accès : public (public) et privé (privé).

Par défaut, si aucun modificateur n'est ajouté, l'accès aux membres du module sera privé.

Pour utiliser les droits public, il est nécessaire d'utiliser le mot-clé pub.

Pour les modules privés, il est possible d'accéder à eux-mêmes ou aux modules inférieurs, mais pas depuis l'extérieur.

mod nation {
    pub mod government {
        pub fn govern() {}
    }
    mod congress {
        pub fn legislate() {}
    }
    
    mod court {
        fn judicial() {
            super::congress::legislate();
        }
    }
}
fn main() {
    nation::government::govern();
}

Ce programme peut être compilé sans problème. Veuillez noter la méthode d'accès à super dans le module court.

Si le module définit une structure, la structure, ainsi que ses champs, sont par défaut privés. Par conséquent, pour utiliser la structure et ses champs du module, il est nécessaire de déclarer pub :

mod back_of_house {
    pub struct Breakfast {
        pub toast: String,
        seasonal_fruit: String,
    }
    impl Breakfast {
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: String::from(toast),
                seasonal_fruit: String::from("peaches"),
            }
        }
    }
}
pub fn eat_at_restaurant() {
    let mut meal = back_of_house::Breakfast::summer("Rye");
    meal.toast = String::from("Wheat");
    println!("Je voudrais {} pain d'épices s'il vous plaît", meal.toast);
}
fn main() {
    eat_at_restaurant()
}

Résultat de l'exécution :

Je voudrais du pain d'épices s'il vous plaît

Les éléments d'une classe d'énumération peuvent contenir des champs, mais ne possèdent pas les mêmes propriétés :

mod SomeModule {
    pub enum Person {
        King {
            name: String
        },
        Quene
    }
}
fn main() {
    let person = SomeModule::Person::King {
        name: String::from("Blue")
    };
    match person {
        SomeModule::Person::King { name } => {
            println!("{}", name);
        }
        _ => {}
    }
}

Résultat de l'exécution :

Blue

Le module difficile à trouver

Les développeurs qui ont utilisé Java détestent souvent le bloc de classe externe le plus en haut — son nom est identique au nom du fichier, car il représente le conteneur du fichier, bien que cela soit fastidieux, nous devons l'écrire une fois pour souligner 'Cette classe est la classe incluse dans le fichier'.

Cependant, cela présente certains avantages : au moins, cela permet aux développeurs de prendre pleinement conscience de l'existence des paquets de classe, et de décrire clairement les relations d'héritage des classes.

Dans Rust, un module est comme un paquet de classe dans Java, mais on peut écrire une fonction principale dès le début du fichier, comment expliquer cela ?

Le contenu de chaque fichier Rust est un module 'difficile à trouver'.

Laissez-nous utiliser deux fichiers pour illustrer cela :

le fichier main.rs

// main.rs
mod second_module;
fn main() {
    println!("C'est le module principal.");
    println!("{}", second_module::message());
}

le fichier second_module.rs

// second_module.rs
pub fn message() -> String {
    String::from("C'est le 2nd module.")
}

Résultat de l'exécution :

C'est le module principal.
C'est 2nd module.

la clé 'use'

L'utilisation de la clé 'use' permet d'introduire l'identifiant du module dans le domaine d'application actuel :

mod nation {
    pub mod government {
        pub fn govern() {}
    }
}
use crate::nation::government::govern;
fn main() {
    govern();
}

Ce programme peut être compilé sans problème.

Parce que le mot-clé use importe l'identifiant govern dans le module actuel, il peut être utilisé directement.

Cela résout le problème de chemin de module local trop long.

Bien sûr, dans certains cas, il peut y avoir deux noms identiques qui doivent également être importés, nous pouvons utiliser le mot-clé as pour ajouter des alias aux identifiants :

mod nation {
    pub mod government {
        pub fn govern() {}
    }
    pub fn govern() {}
}
    
use crate::nation::government::govern;
use crate::nation::govern as nation_govern;
fn main() {
    nation_govern();
    govern();
}

Il y a deux fonctions govern, l'une sous nation et l'autre sous government, nous utilisons as pour renommer la fonction sous nation en nation_govern. Les deux noms peuvent être utilisés simultanément.

Le mot-clé use peut être utilisé avec le mot-clé pub :

mod nation {
    pub mod government {
        pub fn govern() {}
    }
    pub use government::govern;
}
fn main() {
    nation::govern();
}

Référence à la bibliothèque standard

Dictionnaire de bibliothèque standard officielle Rust :https://doc.rust-lang.org/stable/std/all.html

Après avoir appris les concepts de ce chapitre, nous pouvons facilement importer les bibliothèques système pour développer des programmes plus facilement :

use std::f64::consts::PI;
fn main() {
    println!("{}", (PI / 2.0).sin());
}

Résultat de l'exécution :

1

Tous les modules de bibliothèque système sont importés par défaut, donc lors de leur utilisation, il suffit d'utiliser le mot-clé use pour simplifier le chemin pour utiliser facilement.