Discussion du code modules_visibilite

Dans cette partie nous discutons de ce code.

Concepts

Les concepts abordés dans cet exemple sont:

  1. Les modules.
  2. La visibilité.

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

Afin de séparer le code en plusieurs fichiers il est nécessaire de créer un fichier lib.rs qui se trouve dans le même répertoire que le fichier main.rs. Ce répertoire est en général le répertoire src de votre projet. Ici c'est dans projet05/src. Dans notre cas, il 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;

La présence d'un fichier lib.rs indique que vous avez créé une librairie, appelée crate en Rust. Toutes les libraries publiées en Rust sont des crate et peuvent se télécharger depuis le site crates.io.

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 tout le code qui se trouvait dans main.rs dans la partie 4. 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;

Pour 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 champs 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).

Afin d'utiliser 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

  1. Observez ce qui se passe si vous commentez la ligne mod minimum; dans lib.rs et tentez de compiler le code. Que vous dit le compilateur?
  2. Que se passe-t-il, si vous enlevez le mot clé pub de la ligne pub mod io; dans lib.rs et 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 ses membres ou fonctions statiques), 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 points en deux dimension 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 accessible 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() {
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