Ce chapitre explore les alternatives légères aux solutions WebView classiques (Tauri, Electron, Wry) pour créer des applications desktop en Rust. Chaque moteur de rendu possède sa propre philosophie, ses forces et ses compromis. Nous analyserons en profondeur rust-webview, Servo, et Sciter, avant de proposer une matrice de décision complète pour choisir la solution la plus adaptée à vos besoins.
┌─────────────────────────────────────────────────────────────────┐
│ Écosystème des moteurs de rendu légers │
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────────────┐ │
│ │ rust-webview │ │ Servo │ │ Sciter │ │
│ │ │ │ │ │ │ │
│ │ WebView OS │ │ Moteur Rust │ │ HTML/CSS léger │ │
│ │ ~2 Mo │ │ ~15-40 Mo │ │ ~5 Mo │ │
│ │ Minimaliste │ │ Expérimental│ │ Riche en widgets │ │
│ └──────┬───────┘ └──────┬───────┘ └──────────┬───────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Application Desktop Rust + UI Web │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Note pédagogique : Contrairement à Tauri (chapitre 4) ou Wry (chapitre 6) qui représentent des solutions matures et bien maintenues, les alternatives présentées ici occupent des niches spécifiques. Leur connaissance est essentielle pour faire des choix éclairés, notamment dans des contextes embarqués, expérimentaux ou à fortes contraintes de taille.
La crate web-view (souvent appelée rust-webview) est l'un des premiers projets à avoir permis d'embarquer une WebView système dans une application Rust. Elle offre une interface minimaliste pour afficher du contenu HTML/CSS/JS dans une fenêtre native, en utilisant le moteur de rendu du système d'exploitation.
┌──────────────────────────────────────────────────┐
│ Architecture rust-webview │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ Application Rust │ │
│ │ │ │
│ │ ┌─────────────┐ ┌──────────────────┐ │ │
│ │ │ Logique │ │ Callbacks Rust │ │ │
│ │ │ métier │◄──►│ (invoke_handler) │ │ │
│ │ └─────────────┘ └────────┬─────────┘ │ │
│ │ │ │ │
│ └─────────────────────────────┼─────────────┘ │
│ │ │
│ ┌─────────────────────────────▼──────────────┐ │
│ │ WebView Système (FFI) │ │
│ │ │ │
│ │ Windows: MSHTML/Edge Linux: WebKitGTK │ │
│ │ macOS: WKWebView │ │
│ └─────────────────────────────────────────────┘ │
└──────────────────────────────────────────────────┘
| Plateforme | Moteur de rendu | Bibliothèque native |
|---|---|---|
| Windows | MSHTML / Edge WebView2 | webview.dll (MSHTML) |
| macOS | WebKit (WKWebView) | Cocoa / WebKit.framework |
| Linux | WebKitGTK | libwebkit2gtk |
Ajoutez la dépendance dans votre Cargo.toml :
[package]
name = "demo-webview"
version = "0.1.0"
edition = "2021"
[dependencies]
web-view = "0.7"
serde = { version = "1", features = ["derive"] }
serde_json = "1"Attention : Sur Linux, vous devrez installer les dépendances système :
sudo apt install libwebkit2gtk-4.0-dev libgtk-3-dev
Voici une application "Hello World" complète avec rust-webview :
use web_view::*;
fn main() {
// Création de la fenêtre WebView
let webview = web_view::builder()
.title("Mon Application rust-webview")
.content(Content::Html(r#"
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.container { text-align: center; }
button {
padding: 12px 24px;
font-size: 16px;
border: none;
border-radius: 8px;
background: white;
color: #764ba2;
cursor: pointer;
margin-top: 16px;
}
button:hover { opacity: 0.9; }
</style>
</head>
<body>
<div class="container">
<h1>Hello depuis rust-webview !</h1>
<p id="info">Cliquez le bouton pour appeler Rust</p>
<button onclick="appeler_rust()">Appeler Rust</button>
</div>
<script>
function appeler_rust() {
external.invoke(JSON.stringify({
type: "salutation",
nom: "Développeur Rust"
}));
}
</script>
</body>
</html>
"#))
.size(800, 600)
.resizable(true)
.debug(true) // Active les DevTools en développement
.user_data(0u32) // Données utilisateur (compteur ici)
.invoke_handler(|webview, arg| {
// Réception des messages depuis JavaScript
let message: serde_json::Value = serde_json::from_str(arg)
.unwrap_or_default();
if message["type"] == "salutation" {
let nom = message["nom"].as_str().unwrap_or("inconnu");
let compteur = webview.user_data_mut();
*compteur += 1;
// Exécuter du JavaScript depuis Rust
let js = format!(
"document.getElementById('info').textContent = \
'Bonjour {} ! Appel n°{}'",
nom, compteur
);
webview.eval(&js)?;
}
Ok(())
})
.build()
.expect("Impossible de créer la WebView");
// Lancement de la boucle événementielle
webview.run().expect("Erreur durant l'exécution");
}Le pattern de communication dans rust-webview repose sur deux mécanismes :
- JS → Rust : via
external.invoke(string)qui déclenche l'invoke_handler - Rust → JS : via
webview.eval(js_code)qui exécute du JavaScript dans la WebView
┌──────────────┐ ┌──────────────┐
│ JavaScript │ │ Rust │
│ │ │ │
│ external │───invoke(json)────►│ invoke_ │
│ .invoke() │ │ handler() │
│ │ │ │
│ (fonction │◄──eval(js_code)───│ webview │
│ appelée) │ │ .eval() │
└──────────────┘ └──────────────┘
Exemple plus élaboré avec gestion de fichiers :
use web_view::*;
use std::fs;
#[derive(serde::Deserialize)]
#[serde(tag = "cmd")]
enum Commande {
LireFichier { chemin: String },
EcrireFichier { chemin: String, contenu: String },
ListerRepertoire { chemin: String },
}
fn gerer_commande(webview: &mut WebView<()>, arg: &str) -> WVResult {
let cmd: Commande = serde_json::from_str(arg)
.map_err(|e| web_view::Error::Custom(
Box::new(std::io::Error::new(std::io::ErrorKind::Other, e))
))?;
match cmd {
Commande::LireFichier { chemin } => {
match fs::read_to_string(&chemin) {
Ok(contenu) => {
let contenu_escape = contenu
.replace('\\', "\\\\")
.replace('\'', "\\'")
.replace('\n', "\\n");
webview.eval(&format!(
"onFichierLu('{}')", contenu_escape
))?;
}
Err(e) => {
webview.eval(&format!(
"onErreur('Lecture impossible : {}')", e
))?;
}
}
}
Commande::EcrireFichier { chemin, contenu } => {
match fs::write(&chemin, &contenu) {
Ok(_) => webview.eval("onFichierEcrit(true)")?,
Err(e) => webview.eval(&format!(
"onErreur('Écriture impossible : {}')", e
))?,
}
}
Commande::ListerRepertoire { chemin } => {
let entrees: Vec<String> = fs::read_dir(&chemin)
.map(|rd| {
rd.filter_map(|e| e.ok())
.map(|e| e.file_name().to_string_lossy().to_string())
.collect()
})
.unwrap_or_default();
let json = serde_json::to_string(&entrees).unwrap();
webview.eval(&format!("onRepertoireListe({})", json))?;
}
}
Ok(())
}| Aspect | Détail |
|---|---|
| Maintenance | Projet peu actif depuis 2021, supplanté par Wry |
| Moteur Windows | Utilise MSHTML (IE) par défaut — rendu obsolète |
| Communication | String uniquement — sérialisation manuelle requise |
| Multi-fenêtres | Non supporté nativement |
| Fonctionnalités OS | Très limitées (pas de menus, pas de notifications) |
| Sécurité | Pas de sandboxing, pas de CSP intégrée |
Recommandation :
rust-webviewest utile pour comprendre les fondamentaux de l'approche WebView en Rust, mais pour un projet en production, privilégiez Wry (chapitre 6) ou Tauri (chapitre 4) qui en sont les successeurs spirituels directs.
Servo est un moteur de rendu web expérimental développé initialement par Mozilla Research, écrit entièrement en Rust. Contrairement aux WebView système qui délèguent au moteur du système d'exploitation, Servo est le moteur. Cela offre un contrôle total sur le rendu, la parallélisation et la sécurité mémoire.
┌─────────────────────────────────────────────────────────────────┐
│ Architecture de Servo │
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌───────────┐ │
│ │ Parseur │ │ Style │ │ Layout │ │ Rendu │ │
│ │ HTML/CSS │──►│ Résolution│──►│ (Parallèle│──►│ (WebRender│ │
│ │ │ │ (Stylo) │ │ Taffy) │ │ / GPU) │ │
│ └────────────┘ └────────────┘ └────────────┘ └───────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Couche Embedding │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌───────────┐ │ │
│ │ │ Fenêtrage│ │ Événements│ │ Réseau │ │ JavaScript│ │ │
│ │ │ (winit) │ │ (input) │ │ (reqwest) │ │(SpiderMnky│ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └───────────┘ │ │
│ └────────────────────────────────────────────────────────────┘ │
│ │
│ ┌────────────────────────────────────────────────────────────┐ │
│ │ Composants Rust natifs : Rayon, Crossbeam, Serde, etc. │ │
│ └────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
| Composant | Rôle | Technologie |
|---|---|---|
| Stylo | Résolution CSS parallèle | Intégré dans Firefox |
| WebRender | Rendu GPU via OpenGL/Vulkan | Intégré dans Firefox |
| Taffy | Moteur de layout (Flexbox, Grid) | Crate Rust standalone |
| SpiderMonkey | Moteur JavaScript (de Firefox) | C++ via FFI |
| surfman | Abstraction de surface GPU | Rust natif |
| mozjs | Bindings Rust pour SpiderMonkey | FFI Rust ↔ C++ |
Servo peut être compilé comme une bibliothèque et intégré dans votre application Rust. Voici comment configurer un projet basique :
# Cargo.toml — Attention : la compilation de Servo est complexe
[package]
name = "servo-desktop-demo"
version = "0.1.0"
edition = "2021"
[dependencies]
# Servo n'est pas une simple crate — il nécessite un build spécifique.
# On utilise l'API d'embedding de Servo :
servo = { git = "https://github.com/servo/servo", features = ["embedding"] }
winit = "0.30"Avertissement : La compilation de Servo est une opération lourde (30 min à 1h+). Elle nécessite des dépendances système spécifiques et un environnement correctement configuré. Consultez le guide de build officiel.
// NOTE : API simplifiée pour illustrer le concept.
// L'API réelle de Servo évolue fréquemment.
use servo::compositing::windowing::WindowMethods;
use servo::config::opts;
use servo::servo_url::ServoUrl;
use servo::Servo;
/// Structure représentant notre fenêtre d'application.
struct FenetreApp {
titre: String,
largeur: u32,
hauteur: u32,
}
impl FenetreApp {
fn new(titre: &str, largeur: u32, hauteur: u32) -> Self {
Self {
titre: titre.to_string(),
largeur,
hauteur,
}
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Initialisation des options de Servo
let mut opts = opts::default_opts();
opts.headless = false;
opts.url = Some(ServoUrl::parse("data:text/html,<h1>Hello Servo!</h1>")?);
// Création de la fenêtre native avec winit
let event_loop = winit::event_loop::EventLoop::new()?;
let window = winit::window::WindowBuilder::new()
.with_title("Application Servo + Rust")
.with_inner_size(winit::dpi::LogicalSize::new(1024, 768))
.build(&event_loop)?;
// Initialisation de Servo avec notre fenêtre
// (pseudo-code — l'API réelle requiert une implémentation
// complète de WindowMethods et EmbedderMethods)
println!("Servo initialisé avec succès !");
println!("Fenêtre : {}x{}", 1024, 768);
// Boucle événementielle
event_loop.run(move |event, elwt| {
match event {
winit::event::Event::WindowEvent {
event: winit::event::WindowEvent::CloseRequested,
..
} => {
elwt.exit();
}
winit::event::Event::AboutToWait => {
// Ici, on demanderait à Servo de rendre la frame suivante
window.request_redraw();
}
_ => {}
}
})?;
Ok(())
}L'un des atouts majeurs de Servo est sa capacité à paralléliser le rendu web. Là où les moteurs traditionnels (Blink, WebKit) sont majoritairement mono-thread pour le layout et le style, Servo exploite pleinement le parallélisme :
┌─────────────────────────────────────────────────────────────┐
│ Pipeline de rendu parallèle (Servo) │
│ │
│ Thread 1: ──[Parse HTML]──[Style nœud A]──[Layout A]──► │
│ Thread 2: ────────────────[Style nœud B]──[Layout B]──► │
│ Thread 3: ────────────────[Style nœud C]──[Layout C]──► │
│ Thread 4: ────────────────[Style nœud D]──[Layout D]──► │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ Composition │ │
│ │ GPU (rendu) │ │
│ └──────────────┘ │
│ │
│ Versus moteur classique (mono-thread) : │
│ Thread 1: ──[Parse]──[Style A]──[B]──[C]──[D]──[Layout]──►│
└─────────────────────────────────────────────────────────────┘
| Critère | Avantage | Inconvénient |
|---|---|---|
| Langage | 100% Rust — mêmes garanties que votre app | Compilation très longue |
| Parallélisme | Layout et style parallèles | Surcoût mémoire multi-thread |
| Contrôle | Contrôle total du moteur de rendu | API instable, documentation rare |
| Taille | Moteur complet embarqué (~15-40 Mo) | Plus lourd qu'une WebView système |
| Compatibilité web | Support CSS/HTML en progression | Pas encore à parité avec Chrome/Firefox |
| Sécurité | Mémoire sûre grâce à Rust | SpiderMonkey reste en C++ |
| Communauté | Soutien de Linux Foundation depuis 2023 | Communauté plus petite que Chromium/WebKit |
- Kiosques et bornes interactives : contrôle total sur le rendu, pas de dépendance au navigateur système
- Applications embarquées : Servo peut tourner sans serveur X11 (rendu headless)
- Recherche et expérimentation : tester de nouvelles approches de rendu web en Rust
- Produits nécessitant un rendu web cohérent : le même moteur sur toutes les plateformes
Perspective : Depuis son transfert à la Linux Foundation en 2023, Servo connaît un regain d'activité. Le projet vise à devenir un moteur d'embedding viable, ce qui en ferait une alternative 100% Rust à Chromium Embedded Framework (CEF). Restez attentif aux évolutions de l'API d'embedding.
Sciter est un moteur de rendu HTML/CSS propriétaire (mais gratuit pour usage personnel) conçu spécifiquement pour les interfaces d'applications desktop. Contrairement aux WebView classiques qui embarquent un navigateur complet, Sciter n'implémente que le sous-ensemble HTML/CSS utile pour les UI, avec des extensions spécifiques comme le langage de script TIScript (un dialecte de JavaScript).
┌─────────────────────────────────────────────────────────────────┐
│ Architecture Sciter + Rust │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Application Rust │ │
│ │ │ │
│ │ ┌──────────────┐ ┌─────────────────────────────┐ │ │
│ │ │ Logique │ │ rust-sciter (bindings) │ │ │
│ │ │ métier Rust │◄────►│ │ │ │
│ │ │ │ │ EventHandler trait │ │ │
│ │ └──────────────┘ └──────────────┬──────────────┘ │ │
│ └───────────────────────────────────────┼──────────────────┘ │
│ │ FFI │
│ ┌───────────────────────────────────────▼──────────────────┐ │
│ │ Sciter Engine (DLL/dylib/so) │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌────────┐ ┌───────────┐ │ │
│ │ │ Parseur │ │ Layout │ │ Rendu │ │ TIScript │ │ │
│ │ │ HTML/CSS │ │ Engine │ │ Direct │ │ / JS │ │ │
│ │ └──────────┘ └──────────┘ └────────┘ └───────────┘ │ │
│ │ │ │
│ │ Taille totale : ~5 Mo (vs ~80+ Mo pour Chromium) │ │
│ └───────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
[package]
name = "demo-sciter"
version = "0.1.0"
edition = "2021"
[dependencies]
sciter-rs = "0.5"Prérequis : Téléchargez la bibliothèque Sciter native depuis sciter.com et placez le fichier
.dll(Windows),.dylib(macOS) ou.so(Linux) dans votre répertoire de travail ou dans le PATH système.
extern crate sciter;
use sciter::Value;
// Structure qui gère les événements entre l'UI et Rust
struct GestionnaireApp;
impl GestionnaireApp {
// Méthode appelable depuis l'HTML/TIScript
fn calculer_somme(&self, a: f64, b: f64) -> Value {
let resultat = a + b;
println!("[Rust] Calcul : {} + {} = {}", a, b, resultat);
Value::from(resultat)
}
fn obtenir_infos_systeme(&self) -> Value {
let info = format!(
"OS: {} | Arch: {} | CPUs: {}",
std::env::consts::OS,
std::env::consts::ARCH,
std::thread::available_parallelism()
.map(|n| n.get())
.unwrap_or(1)
);
Value::from(info.as_str())
}
}
// Implémentation du trait EventHandler pour recevoir les appels
impl sciter::EventHandler for GestionnaireApp {
sciter::dispatch_script_call! {
fn calculer_somme(f64, f64);
fn obtenir_infos_systeme();
}
}
fn main() {
// Créer la fenêtre Sciter
let mut frame = sciter::Window::new();
// Charger le HTML de l'interface
frame.load_html(br#"
<html>
<head>
<style>
body {
font-family: system;
padding: 20px;
background: #1a1a2e;
color: #e0e0e0;
}
.card {
background: #16213e;
border-radius: 8px;
padding: 20px;
margin: 10px 0;
box-shadow: 0 4px 6px rgba(0,0,0,0.3);
}
input[type=text] {
padding: 8px 12px;
border: 1px solid #0f3460;
border-radius: 4px;
background: #0a0a23;
color: #e0e0e0;
width: 100px;
}
button {
padding: 8px 16px;
background: #e94560;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover { background: #c73750; }
#resultat { color: #53d769; font-weight: bold; }
</style>
<script type="text/tiscript">
function calculer() {
var a = parseFloat($(#val_a).value) || 0;
var b = parseFloat($(#val_b).value) || 0;
var resultat = view.calculer_somme(a, b);
$(#resultat).text = "Résultat : " + resultat.toString();
}
function afficherInfos() {
var infos = view.obtenir_infos_systeme();
$(#infos_systeme).text = infos;
}
// Appeler au chargement
self.ready = function() {
afficherInfos();
};
</script>
</head>
<body>
<h1>🔧 Demo Sciter + Rust</h1>
<div class="card">
<h2>Calculatrice</h2>
<input|text(val_a) placeholder="Valeur A" />
<span> + </span>
<input|text(val_b) placeholder="Valeur B" />
<button onclick="calculer()">Calculer (via Rust)</button>
<p id="resultat">En attente...</p>
</div>
<div class="card">
<h2>Informations système</h2>
<p id="infos_systeme">Chargement...</p>
</div>
</body>
</html>
"#, Some("example://app/main.html"));
// Enregistrer notre gestionnaire d'événements
frame.event_handler(GestionnaireApp);
// Configurer la fenêtre
frame.set_title("Application Sciter + Rust");
frame.run_app();
}Sciter propose un mécanisme de communication plus structuré que rust-webview grâce au trait EventHandler et à la macro dispatch_script_call! :
use sciter::{Value, HELEMENT};
struct GestionnaireAvance {
compteur: std::cell::Cell<u32>,
historique: std::cell::RefCell<Vec<String>>,
}
impl GestionnaireAvance {
fn new() -> Self {
Self {
compteur: std::cell::Cell::new(0),
historique: std::cell::RefCell::new(Vec::new()),
}
}
// Fonctions exposées à TIScript
fn incrementer(&self) -> Value {
let n = self.compteur.get() + 1;
self.compteur.set(n);
self.historique.borrow_mut().push(format!("Incrémenté à {}", n));
Value::from(n as i32)
}
fn obtenir_historique(&self) -> Value {
let hist = self.historique.borrow();
let mut arr = Value::new();
for (i, entry) in hist.iter().enumerate() {
arr.set_item(i, Value::from(entry.as_str()));
}
arr
}
fn rechercher_fichier(&self, motif: String) -> Value {
// Exemple d'opération système depuis l'UI
let resultats: Vec<String> = std::fs::read_dir(".")
.map(|rd| {
rd.filter_map(|e| e.ok())
.filter(|e| {
e.file_name()
.to_string_lossy()
.contains(&motif)
})
.map(|e| e.path().display().to_string())
.collect()
})
.unwrap_or_default();
let json = serde_json::to_string(&resultats).unwrap_or_default();
Value::from(json.as_str())
}
}
impl sciter::EventHandler for GestionnaireAvance {
sciter::dispatch_script_call! {
fn incrementer();
fn obtenir_historique();
fn rechercher_fichier(String);
}
}Sciter offre plusieurs avantages uniques pour les applications desktop :
| Fonctionnalité | Description |
|---|---|
| CSS natifs étendus | Flexbox étendu, flow layouts, animations natives |
| Comportements intégrés | Menus, listes virtualisées, éditeurs riches — sans JS |
| TIScript | Dialecte JavaScript optimisé pour les UI desktop |
| Rendu direct | Pas de couche navigateur — rendu GPU Direct2D / Skia |
| Taille minimale | DLL de ~5 Mo vs ~80+ Mo pour Chromium |
| Mode Lite | Version « Sciter.Lite » encore plus compacte (~3 Mo) |
| Graphiques intégrés | Support SVG, Canvas 2D, vidéo sans dépendances supplémentaires |
- Licence propriétaire : gratuit pour usage personnel/évaluation, licence commerciale requise en production
- Pas de standard web complet : sous-ensemble HTML/CSS — certaines fonctionnalités web manquent
- TIScript ≠ JavaScript : syntaxe similaire mais différences qui complexifient la migration
- Communauté restreinte : moins de ressources, tutoriels et bibliothèques que l'écosystème web
- Futur incertain : développé par une seule personne (Andrew Fedoniouk)
Cas d'usage idéal : Sciter excelle pour les applications desktop qui nécessitent une UI riche avec une empreinte mémoire minimale et ne visent pas le web. C'est un excellent choix pour les outils internes, les utilitaires système ou les applications embarquées où chaque mégaoctet compte.
Cette section synthétise les informations des chapitres 4 à 7 pour vous aider à choisir la bonne solution.
┌────────────────────────────────────────────────────────────────────────────┐
│ Spectre des solutions desktop Rust │
│ │
│ Léger ◄─────────────────────────────────────────────────────► Complet │
│ │
│ rust-webview Wry Sciter Tauri Servo Dioxus Electron │
│ (~2 Mo) (~3 Mo) (~5 Mo) (~8 Mo) (~20 Mo) (~10 Mo) (~150 Mo) │
│ │
│ Simple ◄────────────────────────────────────────────────────► Riche │
│ │
│ Minimal ◄──────────────────────────────────────────────────► Écosystème │
│ (DIY) (Plugins) │
└────────────────────────────────────────────────────────────────────────────┘
| Critère | rust-webview | Servo | Sciter | Wry | Tauri | Electron |
|---|---|---|---|---|---|---|
| Taille binaire | ~2 Mo | ~15-40 Mo | ~5 Mo | ~3 Mo | ~8 Mo | ~150+ Mo |
| RAM au repos | ~15 Mo | ~60 Mo | ~20 Mo | ~25 Mo | ~30 Mo | ~100+ Mo |
| Compat. web | Système | Partielle | Sous-ens. | Système | Système | Chromium |
| Multi-plateforme | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| Multi-fenêtres | ❌ | ✅ | ✅ | ✅ | ✅ | |
| Menus natifs | ❌ | ❌ | ✅ | ✅ | ✅ | ✅ |
| Notifications | ❌ | ❌ | ✅ | ✅ | ||
| Auto-update | ❌ | ❌ | ❌ | ❌ | ✅ | ✅ |
| Écosystème plugins | ❌ | ❌ | ❌ | ✅ | ✅✅ | |
| Sécurité (sandbox) | ❌ | ✅ | ✅ | |||
| Maturité | ✅ Stable | ✅ Stable | ✅ Mature | ✅✅ Mature | ||
| 100% Rust | ✅ | ✅ (moteur) | ❌ (DLL) | ✅ | ✅ | ❌ (Node) |
| Docs / Communauté | ✅ Bonne | ✅✅ | ✅✅✅ |
Utilisez cet arbre pour guider votre choix initial :
Votre projet nécessite-t-il
une application desktop Rust ?
│
▼
┌───────────────────────────────┐
│ Avez-vous besoin d'un rendu │
│ web standard (HTML/CSS/JS) ? │
└──────────────┬────────────────┘
┌───────┴───────┐
OUI NON
│ │
▼ ▼
┌────────────────────┐ Voir Partie III :
│ Taille binaire │ Dioxus, Slint, Iced,
│ critique (< 10 Mo)?│ egui, GTK-rs...
└─────────┬──────────┘
┌──────┴──────┐
OUI NON
│ │
▼ ▼
┌────────────────┐ ┌──────────────────┐
│ Fonctionnalités│ │ Tauri │
│ OS avancées ? │ │ (Recommandé) │
└───────┬────────┘ └──────────────────┘
┌────┴────┐
OUI NON
│ │
▼ ▼
Sciter ┌────────────────────┐
(si UI │ Contrôle total │
riche) │ du moteur requis ? │
└───────┬────────────┘
┌────┴────┐
OUI NON
│ │
▼ ▼
Servo Wry
(expérim.) (minimaliste)
Contexte : Vous développez un outil de monitoring réseau pour usage interne.
Choix recommandé : Wry ou Sciter
// Avec Wry — UI minimaliste pour monitoring
// Voir chapitre 6 pour le code complet
// Critères de décision :
// ✅ Taille < 5 Mo → Wry (~3 Mo) ou Sciter (~5 Mo)
// ✅ Pas besoin d'écosystème plugins
// ✅ Une seule fenêtre suffit
// ❌ Pas besoin d'auto-updateContexte : SaaS avec desktop companion, auto-update, notifications.
Choix recommandé : Tauri (voir chapitre 4)
// Critères de décision :
// ✅ Auto-update nécessaire → Tauri
// ✅ Écosystème de plugins riche
// ✅ Sécurité : sandboxing et CSP
// ✅ Communauté et documentation
// ⚠️ Taille acceptable (~8 Mo)Contexte : Projet de recherche sur le rendu web parallèle.
Choix recommandé : Servo
// Critères de décision :
// ✅ Contrôle total du pipeline de rendu
// ✅ Parallélisation du layout/style
// ✅ 100% Rust (sauf SpiderMonkey)
// ⚠️ API instable — acceptable pour la recherche
// ⚠️ Compilation longue — acceptable pour un prototypeContexte : Interface pour un dispositif industriel, ressources très limitées.
Choix recommandé : Sciter Lite ou Slint (chapitre 9)
// Critères de décision :
// ✅ Empreinte mémoire minimale (< 20 Mo RAM)
// ✅ Taille binaire ultra-compacte (< 5 Mo)
// ✅ Rendu direct sans navigateur
// ✅ Widgets natifs pour interfaces industrielles
// ❌ Pas besoin de compatibilité web standardPour un choix structuré, attribuez des poids (1-5) à chaque critère selon votre projet, puis multipliez par le score de chaque solution :
┌──────────────────────┬───────┬──────────┬───────┬───────┬───────┬────────┐
│ Critère (poids 1-5) │ Poids │rust-wview│ Servo │Sciter │ Wry │ Tauri │
├──────────────────────┼───────┼──────────┼───────┼───────┼───────┼────────┤
│ Taille minimale │ ? │ 5/5 │ 2/5 │ 4/5 │ 5/5 │ 3/5 │
│ Compatibilité web │ ? │ 3/5 │ 2/5 │ 2/5 │ 4/5 │ 4/5 │
│ APIs système │ ? │ 1/5 │ 1/5 │ 3/5 │ 2/5 │ 5/5 │
│ Maturité / Stabilité │ ? │ 2/5 │ 2/5 │ 3/5 │ 4/5 │ 5/5 │
│ Documentation │ ? │ 2/5 │ 2/5 │ 3/5 │ 4/5 │ 5/5 │
│ Performance rendu │ ? │ 3/5 │ 4/5 │ 4/5 │ 3/5 │ 3/5 │
│ 100% Open Source │ ? │ 5/5 │ 5/5 │ 2/5 │ 5/5 │ 5/5 │
│ Sécurité │ ? │ 1/5 │ 3/5 │ 2/5 │ 3/5 │ 5/5 │
├──────────────────────┼───────┼──────────┼───────┼───────┼───────┼────────┤
│ TOTAL (Poids × Note) │ │ Σ = ? │ Σ = ? │ Σ = ? │ Σ = ? │ Σ = ? │
└──────────────────────┴───────┴──────────┴───────┴───────┴───────┴────────┘
Instructions : Remplacez les "?" dans la colonne Poids par vos priorités (1-5).
Multipliez chaque poids par la note, puis additionnez pour obtenir le total.
La solution avec le score le plus élevé est votre meilleur candidat.
| Profil développeur | Solution recommandée | Alternative |
|---|---|---|
| Débutant Rust desktop | Tauri + Yew | Dioxus Desktop |
| Développeur embarqué/IoT | Sciter Lite ou Slint | Servo (expérimental) |
| Startup — MVP rapide | Tauri + Yew | Wry (si ultra-léger) |
| Entreprise — app critique | Tauri (v2) | Electron + napi-rs |
| Chercheur / Académique | Servo | Iced / egui |
| Migration depuis Electron | Tauri | Wry + migration |
| Contrainte taille extrême | Wry | rust-webview (si legacy) |
Ce chapitre a exploré trois alternatives légères pour créer des applications desktop en Rust :
-
rust-webview : la solution historique, minimaliste mais désormais archivée. Elle reste utile pour comprendre les fondamentaux de l'approche WebView mais ne devrait plus être choisie pour un nouveau projet.
-
Servo : le moteur de rendu web 100% Rust, idéal pour la recherche et les cas nécessitant un contrôle total du pipeline de rendu. Son transfert à la Linux Foundation lui donne un avenir prometteur, mais il n'est pas encore prêt pour la production.
-
Sciter : un moteur HTML/CSS léger et spécialisé pour le desktop, offrant un excellent rapport fonctionnalités/taille. Sa licence propriétaire et sa dépendance à un développeur unique en font un choix à évaluer avec prudence.
La section 7.4 fournit des outils de décision concrets — arbre de décision, matrice pondérée, scénarios — pour choisir la solution la plus adaptée à votre contexte.
- Il n'existe pas de solution universelle : chaque moteur répond à des contraintes spécifiques
- Tauri reste le choix par défaut pour la majorité des projets desktop Rust basés sur le web
- La taille binaire n'est pas le seul critère : maturité, sécurité et écosystème comptent autant
- L'écosystème Rust desktop évolue rapidement : réévaluez vos choix régulièrement
Chapitre suivant : Chapitre 8 — Dioxus Desktop : le concurrent direct
Dans la Partie III, nous quittons les solutions WebView pour explorer les frameworks de rendu natif en Rust. Dioxus Desktop, avec son approche React-like et son rendu via Tao/Wry, représente un pont naturel entre le monde web et le desktop natif.