Rust

Bienvenu sur ma page dédié à l'apprentissage du code avec le langage Rust ! J'apprends ce langage sur mon temps libre en plus de mes études. Si vous voulez apprendre le Rust avec moi c'est ici ! :)

Si après avoir lu cet article, vous vous demandez encore pourquoi apprendre à coder, comment apprendre la programmation ou avec qui, jetez un oeil à ma page explicative ;)

Rust est un langage assez peu connu. Il est décrit comme rapide, fiable et productif. C'est un langage compilé similaire aux langages C ou C++. Il est utilisé pour le développement système, réseau, le codage d'applications, aussi bien desktop que web (avec son framework webassembly), ou encore le développement de systèmes embarqués.

Voici un petit guide qui vous fournira une introduction de haut niveau au langage de programmation Rust. J'aborderais les bases de la syntaxe, les types de données, les opérateurs et le flux de contrôle, ainsi que des sujets plus avancés tels que la gestion de la mémoire, la correspondance des modèles, et plus encore. 

1. Introduction à Rust

Rust est un langage de programmation de systèmes développé par Mozilla Corporation. Il permet aux développeurs d’écrire du code qui peut s’exécuter rapidement et en toute sécurité sur une variété d’architectures matérielles. Rust est conçu pour être rapide et sûr de mémoire, et il est livré avec un large éventail de fonctionnalités telles que la sécurité de mémoire, la sécurité de type, la gestion efficace de la mémoire, la sécurité des processus fils, et plus encore. 


2. Syntaxe 

La syntaxe de Rust est similaire à C et C++, mais elle est légèrement plus élevée. Les variables et les fonctions sont déclarées en utilisant les mots clés let et fn. Le flux de contrôle est réalisé en utilisant les conditions if/else/else if et les itérateurs while loop et for loop. Les types de données tels que les entiers, les floats et les booléens sont supportés, et les chaînes sont immuables. 


3. Types de données 

Rust supporte plusieurs types de données primitives, y compris les entiers (i32, i64, u32, u64), les nombres à virgule flottante (f32, f64), et les booléens. Rust prend également en charge des types de données plus complexes tels que les tuples, structs, enums, et plus encore. 


4. Opérateurs 

Rust a des opérateurs arithmétiques et booléens, y compris addition (+), soustraction (-), multiplication (*), division (/), module (%) et comparaison (==, !=, >, <, >=, <=). Les opérateurs booléens incluent && (and), || (or), et ! (not). 


5. Contrôle du débit 

Le flux de contrôle dans Rust est réalisé en utilisant les instructions if/else, les instructions switch et les constructions de boucles. Les expressions if/else vous permettent de prendre des décisions en fonction de certaines conditions, tandis que les conditions switch vous permettent de choisir l’une des plusieurs options. Les constructions de boucles telles que while et for loops vous permettent de répéter le code. 


6. Gestion de la mémoire 

Rust assure une gestion efficace de la mémoire en permettant aux développeurs de déterminer quand une variable n’est plus nécessaire. Rust utilise un système appelé « propriété », qui permet de suivre les variables actuellement utilisées et de libérer de la mémoire lorsqu’elle n’est plus nécessaire. La mémoire est également gérée en utilisant des « durées de vie », qui permettent aux développeurs de définir quand un objet peut être créé et détruit. 


7. Appariement des modèles 

Le pattern matching est un outil puissant dans Rust pour comparer des types de données complexes. C’est une façon de faire correspondre différentes valeurs aux données stockées dans un format particulier. Il peut être utilisé pour faire des comparaisons, extraire des données de structures, ou même exécuter du code basé sur certaines conditions. 


8. Traitement des erreurs 

Rust a de puissantes capacités de gestion des erreurs, ce qui facilite la gestion des erreurs qui peuvent se produire dans votre code. Rust utilise des « types de résultats » qui vous permettent de traiter les erreurs de façon sécuritaire et uniforme. Cette technique de gestion des erreurs facilite également le refactoring, car les modifications apportées à une partie du code n’affecteront pas d’autres parties du code. 

Quelques snippets de code pour visualiser la syntaxe et la structure du langage

//definition d'une constante

const MAX_POINTS: u32 = 100_000;


//utilisation d'un test if

let number = 6;


if number % 4 == 0 {

    println!("number is divisible by 4");

} else if number % 3 == 0 {

    println!("number is divisible by 3");

} else {

    println!("number is not divisible by 3 or 4");

}


//definition d'une fonction

fn greet(name: &str) {

    println!("Hello, {}!", name);

}


//appel de la fonction

greet("Bob");

Voyons maintenant le grand classique algorithmique des tours de Hanoï

fn hanoi(num_disks: usize, src: char, dst: char, temp: char) {

    if num_disks > 0 {

        hanoi(num_disks - 1, src, temp, dst);

        println!("Move disk from {} to {}.", src, dst);

        hanoi(num_disks - 1, temp, dst, src);

    }

}


fn main() { 

    let num_disks = 3;

    hanoi(num_disks, 'A', 'B', 'C');

}

La manipulation de tableaux

fn array_manipulation() {

    let mut arr = [1,2,3,4,5];


    // add 3 to each array items

    for i in 0..arr.len() {

        arr[i] += 3;

    }


    // print the new array

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


    // reverse array elements

    arr.reverse();


    // print the reversed array

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

}

Et enfin un petit exemple de gestion de la mémoire

// This example creates a memory pool of 4 slots, then allocates from that pool and then deallocates when no longer needed.


use std::mem::{size_of};


// A simple memory pool that holds 4 slots of size u32

struct MemoryPool {

    slots: [Option<u32>; 4],

}


impl MemoryPool {

    fn alloc(&mut self) -> Option<&mut u32> {


        // Find a vacant slot

        for slot in self.slots.iter_mut() {

            if slot.is_none() {

                // Allocate the slot

                *slot = Some(0); 

                return slot.as_mut();

            }

        }


        // No vacant slots available

        None

    }


    fn dealloc(&mut self, slot: &mut u32) {


        // Find this slot and make it vacant

        for slots in self.slots.iter_mut() {

            if slots.as_mut() == Some(slot) {

                *slots = None;

            }

        }

    }

}


fn main() {

    let mut pool = MemoryPool { slots: [None; 4] };


    let mut slot1 = pool.alloc().unwrap();

    *slot1 = 42;


    let mut slot2 = pool.alloc().unwrap();

    *slot2 = 43;


    {

        let mut slot3 = pool.alloc().unwrap();

        *slot3 = 44;


        println!("slot 1 = {}, slot 2 = {}, slot 3 = {}", *slot1, *slot2, *slot3);

        // Deallocates the slot once it falls out of scope

        pool.dealloc(slot3);

    }


    // The returned slot should now be vacant

    assert_eq!(pool.alloc(), Some(&mut 0));


    println!("pool size = {} bytes", size_of::<MemoryPool>());

}

Voici quelques différences notamment entre Rust et C concernant la gestion de la mémoire.

En C, la gestion de la mémoire se fait manuellement. Le programmeur doit allouer et désaffecter manuellement la mémoire du tas ou de la pile, ce qui peut souvent conduire à des erreurs et des problèmes tels que des fuites de mémoire.


Rust utilise un système appelé Automatic Reference Counting (ARC) pour gérer la mémoire. Il permet une gestion efficace de la mémoire avec un minimum d’effort de la part du programmeur car il suit et désaffecte automatiquement la mémoire inutilisée. Puisque Rust a des contrôles stricts de compilateur et un système de type sophistiqué, il assure que la mémoire est manipulée correctement tout en empêchant les erreurs telles que les pointeurs pendants et doubles libres.

Apprendre Rust n'est pas si compliqué si on y consacre du temps..