Discussion du code modules_visibilite
Dans cette partie nous discutons de ce code.
Concepts
Les concepts abordés dans cet exemple sont:
Discussion
Jusqu'ici, tout le code était contenu dans le fichier main.rs. Au fur et à mesure que le code devient plus complexe et long, il est nécessaire de le séparer en plusieurs fichiers (modules) et de gérer
la visibilité des structures, types énumérés, fonctions, etc. Nous allons voir dans ce chapitre comment
cela se passe pour le Rust. Pour plus d'informations vous pouvez vous référer à La Bible du Rust.
Les modules
Un module se déclare avec le mot-clé mod. On peut le faire directement dans main.rs (mod io;, le code allant dans io.rs), sans aucun autre fichier. Ici on choisit plutôt de déclarer les modules dans un fichier lib.rs placé à côté de main.rs (dans src/), pour que le code soit réutilisable à la fois par main.rs, par les tests et par d'autres crates. Ce lib.rs contient très peu de lignes
/*!
modules_visibilite illustrates the concepts of **modules** and **visibility**.
*/
// The size of the tab
const SIZE: usize = 9;
pub mod io;
mod minimum;
pub mod something_or_nothing;
Une crate est une unité de compilation. Un main.rs produit une crate binaire (un exécutable) ;
un lib.rs produit une crate bibliothèque, réutilisable et publiable sur crates.io.
Les deux peuvent contenir des modules.
On voit qu'il y a dans le fichier lib.rs la définition de la constante SIZE, ainsi que trois lignes
contenant le mot-clé mod qui indique la présence d'un module. Par défaut, le compilateur Rust va aller chercher
le contenu de ces modules dans les fichiers io.rs, minimum.rs, et something_or_nothing.rs
(ou io/mod.rs, minimum/mod.rs, et something_or_nothing/mod.rs). Dans ce chapitre, nous avons simplement réparti dans des modules tout le code qui se trouvait dans le main.rs du chapitre La propriété. Le mot-clé
pub indique la visibilité du module à l'intérieur de votre librairie.
Ainsi, le module minimum n'est pas exposé à vos utilisatrices et utilisateurs, alors que io et something_or_nothing le sont. Nous verrons un peu plus bas les règles sur la visibilité.
Afin d'utiliser les fonctions définies dans notre librairie dans notre programme principal (le main.rs),
comme dans le code suivant
use modules_visibilite::io;
use modules_visibilite::something_or_nothing::find_min;
il faut importer les modules avec la syntaxe suivante
use nom_de_la_crate::nom_du_module;
où le nom_de_la_crate est défini dans le fichier modules_visibilite/Cargo.toml (le champ name),
le nom du module ici est io et chaque module est séparé par le symbole ::.
On a également importé la fonction find_min spécifiquement
avec la syntaxe
use nom_de_la_crate::nom_du_module::nom_de_la_fonction;
Ce n'est pas fait dans cet exemple, mais il est tout à fait possible de définir des sous-modules (voir Le Livre).
Un module peut aussi être défini en ligne, entre accolades, sans fichier séparé :
mod geometrie { pub struct Point { pub x: f64, pub y: f64 } pub fn origine() -> Point { Point { x: 0.0, y: 0.0 } } } fn main() { let o = geometrie::origine(); println!("({}, {})", o.x, o.y); }
Afin de partager des fonctions entre les modules, il faut également les importer comme dans le module something_or_nothing qui nécessite l'import du trait Minimum à l'aide de la syntaxe
use crate::minimum::Minimum;
On voit la nécessité d'utiliser le mot-clé crate pour indiquer que le module est importé depuis
l'intérieur de notre librairie. Puis il faut suivre l'arborescence habituelle avec le module minimum
et le trait Minimum le tout séparé par des séparateurs, ::.
Observations
- Observez ce qui se passe si vous commentez la ligne
mod minimum;danslib.rset tentez de compiler le code. Que vous dit le compilateur? - Que se passe-t-il, si vous enlevez le mot-clé
pubde la lignepub mod io;danslib.rset tentez de compiler le code? Quel message s'affiche?
On constate que pour la partie 1, le compilateur n'est pas content, car il ne trouve pas
le module minimum dans notre crate
error[E0432]: unresolved import `crate::minimum`
--> src/something_or_nothing.rs:2:12
|
2 | use crate::minimum::Minimum;
| ^^^^^^^ could not find `minimum` in the crate root
For more information about this error, try `rustc --explain E0432`.
error: could not compile `modules_visibilite` (lib) due to previous error
Pour la partie 2, on a deux messages un peu différents
Compiling modules_visibilite v0.1.0 (/home/orestis/git/projects/rust-101/codes/rust_lang/modules_visibilite)
warning: function `read_command_line` is never used
--> src/io.rs:2:8
|
2 | pub fn read_command_line() -> [i32; crate::SIZE] {
| ^^^^^^^^^^^^^^^^^
|
= note: `#[warn(dead_code)]` on by default
warning: function `print_tab` is never used
--> src/io.rs:8:8
|
8 | pub fn print_tab(tab: &[i32; crate::SIZE]) {
| ^^^^^^^^^
warning: `modules_visibilite` (lib) generated 2 warnings
error[E0603]: module `io` is private
--> src/main.rs:2:13
|
2 | use modules_visibilite::io;
| ^^ private module
|
note: the module `io` is defined here
--> /home/orestis/git/projects/rust-101/codes/rust_lang/modules_visibilite/src/lib.rs:9:1
|
9 | mod io;
| ^^^^^^
For more information about this error, try `rustc --explain E0603`.
error: could not compile `modules_visibilite` (bin "modules_visibilite") due to previous error
Le compilateur commence par nous prévenir par des warnings que les fonctions print_tab et read_command_line() ne sont jamais utilisées. Puis, nous avons un message nous prévenant que io est privé.
Visibilité
Par défaut, Rust rend privées toutes les structures (et leurs champs, méthodes et fonctions associées), traits, fonctions, etc. Cela signifie qu'elles ne sont pas visibles en dehors du
module dans lequel elles sont définies. Pour les rendre visibles (en dehors du module dans lequel elles sont définies), il faut les rendre publiques à l'aide du préfixe pub.
Il y a plusieurs exemples de l'utilisation dans ce chapitre.
- Pour les fonctions:
pub fn print_tab(tab: &[i32; crate::SIZE]) {
for t in tab {
print!("{} ", t);
}
println!();
}
on voit qu'on préfixe pub devant le mot-clé fn pour rendre la fonction publique. Si on le retire, le compilateur donnera le message d'erreur suivant
error[E0603]: function `print_tab` is private
--> src/main.rs:11:9
|
11 | io::print_tab(&tab);
| ^^^^^^^^^ private function
|
note: the function `print_tab` is defined here
--> /home/orestis/git/projects/rust-101/codes/rust_lang/modules_visibilite/src/io.rs:9:1
|
9 | fn print_tab(tab: &[i32; crate::SIZE]) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For more information about this error, try `rustc --explain E0603`.
- Pour un type énuméré:
#[derive(Clone, Copy)]
pub enum SomethingOrNothing<T> {
Nothing,
Something(T),
}
où on préfixe le mot-clé enum avec un pub.
- Pour les méthodes
impl<T: std::fmt::Display> SomethingOrNothing<T> {
pub fn print(&self) {
match self {
SomethingOrNothing::Nothing => println!("Nothing."),
SomethingOrNothing::Something(val) => println!("Something is: {}", val),
}
}
}
où comme pour les fonctions, on préfixe fn avec un pub.
- Pour les traits:
pub trait Minimum: Copy {
fn min(self, rhs: Self) -> Self;
}
il faut noter que seule la définition du trait a besoin d'être publique. L'implémentation pour un type particulier n'a pas besoin de l'être.
Remarque
Tous les champs d'une structure (même publique) sont privés par défaut. Ainsi une structure Point contenant les coordonnées d'un point en deux dimensions se définit comme
#![allow(unused)] fn main() { pub struct Point { x: f32, y: f32, } }
En dehors du module où cette structure serait définie, il est impossible d'accéder aux champs de la structure.
L'instantiation et initialisation d'un Point
let p = Point { x: 1.0, y: 0.2 };
donnerait une erreur de compilation, car x et y sont privés. Il est donc nécessaire de préfixer les champs publics par un pub pour qu'ils soient accessibles en dehors du module.
error[E0451]: field `x` of struct `Point` is private
--> src/main.rs:15:43
|
15 | let p = something_or_nothing::Point { x: 1.0, y: 0.5 };
| ^^^^^^ private field
error[E0451]: field `y` of struct `Point` is private
--> src/main.rs:15:51
|
15 | let p = something_or_nothing::Point { x: 1.0, y: 0.5 };
| ^^^^^^ private field
For more information about this error, try `rustc --explain E0451`.
On aurait pour rendre x et y publics besoin de définir la structure comme
#![allow(unused)] fn main() { pub struct Point { pub x: f32, pub y: f32, } }
Rustlings
Les rustlings à faire dans ce chapitre sont les suivants:
Les modules
$ rustlings run modules1
$ rustlings run modules2
$ rustlings run modules3