Travailler avec WebAssembly et Rust : méthodes avancées d'intégration dans le web

Formateur Python en ligne pour débutants

Apprenez Python facilement sans surcharge théorique. Résolvez des tâches pratiques avec vérification automatique et écrivez du code directement dans le navigateur.

Start Course

Introduction : pourquoi Rust et WebAssembly au niveau avancé

\n

WebAssembly (Wasm) et Rust sont une combinaison qui permet de transférer des tâches computationnellement lourdes de JavaScript vers un code natif haute performance fonctionnant dans le navigateur. Au niveau de base, vous avez peut-être déjà découvert la compilation de fonctions simples. Cependant, pour créer des produits réels — moteurs de jeu, processeurs d'images, analyseurs syntaxiques ou bibliothèques cryptographiques — une compréhension approfondie de la gestion de la mémoire, du transfert de structures de données complexes et de l'interaction avec l'environnement externe est nécessaire.

\n

Dans cet article, nous examinerons les techniques avancées de travail avec WebAssembly en Rust : de la gestion manuelle de la mémoire à la création d'applications hybrides où Rust et JavaScript fonctionnent comme un tout. Le matériel est destiné aux développeurs déjà familiarisés avec les bases de Rust et Wasm.

\n\n

1. Gestion de la mémoire : mémoire linéaire et allocateurs

\n

Contrairement à JavaScript, WebAssembly utilise une mémoire linéaire — un tableau plat d'octets. Le code Rust compilé en Wasm travaille directement avec cette mémoire. Pour un travail efficace, il est nécessaire de comprendre comment allouer et libérer la mémoire manuellement.

\n\n

1.1. Allocation manuelle de mémoire avec wasm_bindgen

\n

La bibliothèque wasm-bindgen fournit des abstractions pratiques, mais au niveau avancé, un accès direct à la mémoire est souvent nécessaire. Considérons un exemple :

\n
use wasm_bindgen::prelude::*;\nuse std::mem;\nuse std::slice;\n\n#[wasm_bindgen]\npub fn allocate_buffer(size: usize) -> *mut u8 {\n    let mut buf = Vec::with_capacity(size);\n    let ptr = buf.as_mut_ptr();\n    mem::forget(buf); // empêche la suppression du vecteur\n    ptr\n}\n\n#[wasm_bindgen]\npub fn deallocate_buffer(ptr: *mut u8, size: usize) {\n    unsafe {\n        let _ = Vec::from_raw_parts(ptr, 0, size);\n    }\n}\n\n#[wasm_bindgen]\npub fn fill_buffer(ptr: *mut u8, value: u8, len: usize) {\n    let slice = unsafe { slice::from_raw_parts_mut(ptr, len) };\n    for byte in slice.iter_mut() {\n        *byte = value;\n    }\n}
\n

Ici, nous allouons de la mémoire via Vec, nous « oublions » le vecteur (pour que Rust ne supprime pas le tampon) et nous passons le pointeur à JavaScript. La fonction deallocate_buffer restaure le vecteur et libère correctement la mémoire.

\n\n

1.2. Utilisation d'un allocateur personnalisé

\n

Pour les projets complexes, il est conseillé de remplacer l'allocateur standard par un allocateur plus prévisible. Par exemple, wee_alloc est un petit allocateur optimisé pour Wasm :

\n
// dans Cargo.toml\n// [dependencies]\n// wee_alloc = "0.4"\n\n#[global_allocator]\nstatic ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
\n

Cela réduira la taille du fichier .wasm final et accélérera l'allocation de mémoire dans le navigateur.

\n\n

2. Transfert de structures de données complexes entre Rust et JavaScript

\n

Au niveau avancé, vous ne vous limitez pas aux nombres et aux chaînes de caractères. Il est nécessaire de transférer des tableaux, des structures et même des fermetures.

\n\n

2.1. Structures avec #[wasm_bindgen]

\n

Vous pouvez exporter des structures Rust entières en tant que classes JavaScript :

\n
#[wasm_bindgen]\npub struct Matrix {\n    rows: usize,\n    cols: usize,\n    data: Vec<f64>,\n}\n\n#[wasm_bindgen]\nimpl Matrix {\n    #[wasm_bindgen(constructor)]\n    pub fn new(rows: usize, cols: usize) -> Matrix {\n        Matrix {\n            rows,\n            cols,\n            data: vec![0.0; rows * cols],\n        }\n    }\n\n    pub fn get(&self, row: usize, col: usize) -> f64 {\n        self.data[row * self.cols + col]\n    }\n\n    pub fn set(&mut self, row: usize, col: usize, val: f64) {\n        self.data[row * self.cols + col] = val;\n    }\n\n    pub fn multiply(&self, other: &Matrix) -> Result<Matrix, String> {\n        if self.cols != other.rows {\n            return Err("Dimensions incompatibles".to_string());\n        }\n        // ... implémentation de la multiplication matricielle\n        let result = Matrix::new(self.rows, other.cols);\n        Ok(result)\n                

Blogs

Book Recommendations