🔝 Retour au Sommaire
La section précédente a montré comment cxx fournit un bridge type-safe entre C++ et Rust, avec une interface déclarée manuellement dans un bloc #[cxx::bridge]. Cette approche est idéale quand l'interface est de taille raisonnable et bien définie. Mais elle rencontre ses limites face à une réalité industrielle courante : une API C++ existante de grande taille — des centaines de fonctions, des dizaines de classes, des structures imbriquées — qu'on veut consommer depuis Rust sans réécrire chaque déclaration à la main.
C'est le problème que résout autocxx. Développé par Adrian Taylor (Google), autocxx parse directement les headers C++ et génère automatiquement les bindings Rust correspondants. Le développeur pointe vers un header, indique les types et fonctions qu'il veut utiliser, et autocxx produit le code de glue nécessaire — en s'appuyant sur cxx en interne pour le mécanisme de pont.
L'analogie avec la section Python est directe : autocxx est à cxx ce que SWIG est à pybind11 — une approche par génération automatique qui sacrifie un peu de contrôle fin au profit de la productivité sur les grandes API.
L'écosystème Rust offre trois approches pour consommer du C++ :
bindgen (section 43.3.1.7) parse les headers C/C++ et génère des déclarations extern "C" brutes en Rust. Le résultat est du FFI bas niveau : pointeurs bruts, unsafe partout, aucune abstraction. bindgen fonctionne bien pour les API C pures, mais produit des bindings C++ inutilisables en pratique (pas de support des méthodes, des constructeurs/destructeurs, des conteneurs STL).
cxx (section 43.3.2) fournit un bridge type-safe mais déclaratif : le développeur écrit manuellement la description de l'interface dans un bloc #[cxx::bridge]. Le résultat est élégant et sûr, mais l'effort initial est proportionnel à la taille de l'API.
autocxx combine les deux : il utilise un parser C++ (basé sur bindgen/libclang) pour comprendre les headers, puis génère automatiquement du code compatible cxx. Le résultat est un binding qui hérite de la sécurité de cxx avec le confort d'une génération automatique.
| Critère | bindgen | cxx | autocxx |
|---|---|---|---|
| Input | Headers C/C++ | Bloc #[cxx::bridge] (Rust) |
Headers C++ + directives Rust |
| Approche | Parse → FFI brut | Déclaratif → glue générée | Parse → glue cxx générée |
| Support C pur | Excellent | Via extern "C" |
Via bindgen interne |
| Support classes C++ | Non (struct layout seul) | Types opaques, méthodes | Constructeurs, méthodes, héritage basique |
| Support STL | Non | CxxString, CxxVector, UniquePtr |
std::string, std::vector, unique_ptr |
| Support templates | Non | Non (instanciations manuelles) | Partiel (instanciations concrètes) |
Surface unsafe |
Omniprésente | Limitée | Limitée (similaire à cxx) |
| Contrôle fin | Maximal | Élevé | Modéré |
| Effort initial | Faible (générateur) | Proportionnel à l'API | Faible (directives minimales) |
| Maintenabilité | Fragile | Solide | Bonne (suivi des headers) |
| Maturité (2026) | Très mature | Mature | En maturation active |
Le cas d'usage principal est clair : consommer une API C++ existante de grande taille depuis Rust, sans la réécrire ni la wrapper manuellement. Les situations typiques :
- Intégration d'une bibliothèque tierce C++ dont on ne contrôle pas l'API.
- Migration progressive où le code Rust doit appeler du code C++ existant (et pas l'inverse, qui relève davantage de
cxx). - Prototypage rapide d'une intégration C++/Rust avant de raffiner l'interface avec
cxx. - Bases de code avec des centaines de fonctions/classes où l'écriture manuelle du bridge
cxxserait prohibitive.
Headers C++ (.h)
│
▼
libclang (parser)
│
├──→ bindgen (types C, structs, enums, constantes)
│
└──→ autocxx (classes, méthodes, constructeurs, STL)
│
├──→ Code Rust généré (wrappers safe, UniquePtr, etc.)
│
└──→ Code C++ de glue (wrappers extern "C")
│
▼
Compilé avec cc / cxx-build
autocxx utilise libclang pour parser le header C++ en un AST (Abstract Syntax Tree) complet. Il analyse les classes, les méthodes, les constructeurs, les types de retour, les paramètres, et génère un ensemble de wrappers qui permettent au code Rust d'appeler le code C++ à travers une interface safe.
En interne, autocxx produit du code compatible avec le mécanisme de cxx. Les types UniquePtr, CxxString, Pin<&mut T> de cxx sont réutilisés. L'avantage est double : le code généré bénéficie de la sécurité de cxx, et les bindings autocxx et cxx manuels peuvent coexister dans le même projet.
Prenons une classe C++ typique :
// geometry.h
#pragma once
#include <string>
#include <cstdint>
class Rectangle {
public:
Rectangle();
Rectangle(double width, double height);
~Rectangle();
double area() const;
double perimeter() const;
void scale(double factor);
double width() const;
double height() const;
void set_dimensions(double w, double h);
std::string describe() const;
static Rectangle unit_square();
private:
double width_;
double height_;
};Pour cette classe, autocxx génère automatiquement :
- Un type opaque
Rectanglemanipulable viaUniquePtr<Rectangle>. - Un wrapper pour chaque constructeur (via
Rectangle::new(),Rectangle::new1(w, h)). - Un wrapper pour chaque méthode publique.
- Un wrapper pour la méthode statique.
- La gestion automatique du destructeur via
UniquePtr.
Le code Rust consommateur ressemble à ceci (on verra la syntaxe exacte dans les sections suivantes) :
let rect = ffi::Rectangle::new1(3.0, 4.0).within_unique_ptr();
println!("Area: {}", rect.area());
println!("Perimeter: {}", rect.perimeter());
println!("{}", rect.describe().to_str().unwrap()); Le développeur n'a pas écrit de bloc #[cxx::bridge]. Il n'a pas déclaré manuellement chaque méthode. Il a pointé vers le header et indiqué vouloir utiliser Rectangle.
# Cargo.toml
[package]
name = "myproject"
version = "0.1.0"
edition = "2021"
[dependencies]
autocxx = "0.27"
cxx = "1.0"
[build-dependencies]
autocxx-build = "0.27"
miette = { version = "7", features = ["fancy"] } # Messages d'erreur lisibles autocxx nécessite libclang pour parser les headers C++ :
# Ubuntu
sudo apt install libclang-dev llvm-dev
# Vérifier
llvm-config --version # 15+ recommandé// build.rs
fn main() -> miette::Result<()> {
let path = std::path::PathBuf::from("src");
let mut builder = autocxx_build::Builder::new("src/main.rs", [&path, &"include".into()])
.build()?;
builder
.flag_if_supported("-std=c++20")
.file("src/geometry.cpp") // Code C++ métier
.compile("myproject");
println!("cargo:rerun-if-changed=src/main.rs");
println!("cargo:rerun-if-changed=src/geometry.cpp");
println!("cargo:rerun-if-changed=include/geometry.h");
Ok(())
}Le Builder::new prend deux arguments : le fichier Rust contenant le bloc include_cpp! (voir ci-dessous), et la liste des répertoires d'include. autocxx scanne le fichier Rust pour trouver les directives, parse les headers référencés, et génère le code de glue.
myproject/
├── Cargo.toml
├── build.rs
├── include/
│ └── geometry.h # Headers C++ existants
├── src/
│ ├── geometry.cpp # Implémentation C++ existante
│ └── main.rs # Code Rust avec directives autocxx
Le cœur d'autocxx côté Rust est la macro include_cpp! :
// src/main.rs
use autocxx::prelude::*;
include_cpp! {
#include "geometry.h"
safety!(unsafe_ffi)
generate!("Rectangle")
}
fn main() {
// Constructeur par défaut
let rect = ffi::Rectangle::new().within_unique_ptr();
println!("Area: {}", rect.area());
// Constructeur avec paramètres
let rect2 = ffi::Rectangle::new1(3.0, 4.0).within_unique_ptr();
println!("Area: {}", rect2.area());
println!("Description: {}", rect2.describe().to_str().unwrap());
// Méthode statique
let unit = ffi::Rectangle::unit_square().within_unique_ptr();
println!("Unit area: {}", unit.area());
}Anatomie du bloc include_cpp! :
#include "geometry.h" — indique le header C++ à parser. Plusieurs #include sont possibles.
safety!(unsafe_ffi) — déclare la politique de sécurité. unsafe_ffi signifie "je fais confiance au code C++ pour ne pas causer de comportement indéfini quand il est appelé correctement". C'est le mode le plus courant. L'alternative safety!(unsafe) est plus restrictive et force le marquage unsafe sur chaque appel.
generate!("Rectangle") — indique à autocxx de générer les bindings pour le type Rectangle. Sans cette directive, aucun binding n'est généré — autocxx ne produit que ce qu'on lui demande explicitement.
Après expansion, le bloc include_cpp! crée un module ffi contenant les types et fonctions générés. Ce module est accessible comme un module Rust normal :
// Accès aux types générés
let r: cxx::UniquePtr<ffi::Rectangle> = ffi::Rectangle::new().within_unique_ptr();
// Les méthodes const sont accessibles via &self sur UniquePtr
let area: f64 = r.area();
// Les méthodes non-const nécessitent Pin<&mut T>
let mut r = ffi::Rectangle::new1(3.0, 4.0).within_unique_ptr();
r.pin_mut().scale(2.0); C++ permet la surcharge de constructeurs. Rust ne permet pas la surcharge de fonctions. autocxx résout ce conflit en numérotant les constructeurs :
| C++ | Rust (autocxx) |
|---|---|
Rectangle() |
Rectangle::new() |
Rectangle(double, double) |
Rectangle::new1(w, h) |
Rectangle(const Rectangle&) |
Rectangle::new2(other) (copy ctor) |
Les noms new1, new2, etc. ne sont pas élégants. Pour les cas où la lisibilité compte, on peut combiner autocxx avec des wrappers Rust manuels (voir section 43.3.3.7).
En C++, une méthode non-const modifie potentiellement l'objet. En Rust, cela correspond à un emprunt mutable &mut self. Comme les objets C++ gérés par UniquePtr ne doivent pas être déplacés en mémoire (ils peuvent contenir des pointeurs internes), autocxx exige Pin<&mut T> pour les méthodes mutables :
let mut rect = ffi::Rectangle::new1(3.0, 4.0).within_unique_ptr();
// Méthode const — &self, accessible directement
let a = rect.area();
// Méthode non-const — Pin<&mut self>, nécessite .pin_mut()
rect.pin_mut().scale(2.5);
rect.pin_mut().set_dimensions(10.0, 20.0); Le .pin_mut() est l'appel qui obtient un Pin<&mut Rectangle> depuis un UniquePtr<Rectangle>. C'est une contrainte syntaxique imposée par le modèle de sécurité de cxx/autocxx, pas un choix esthétique.
include_cpp! {
#include "geometry.h"
#include "physics.h"
safety!(unsafe_ffi)
// Types
generate!("Rectangle")
generate!("Circle")
generate!("PhysicsEngine")
// Fonctions libres
generate!("compute_distance")
generate!("create_default_engine")
}Chaque generate! demande la génération des bindings pour le type ou la fonction nommé. Les types dépendants (types utilisés dans les signatures des méthodes) sont inclus automatiquement s'ils sont des types primitifs ou des types STL supportés. Les types personnalisés dépendants doivent être générés explicitement.
include_cpp! {
#include "geometry.h"
safety!(unsafe_ffi)
generate_all!()
}generate_all!() génère les bindings pour tous les types et fonctions trouvés dans le header. C'est pratique pour le prototypage, mais déconseillé en production : cela inclut des types internes, augmente le temps de compilation, et peut générer des conflits avec des types non supportés.
Par défaut, autocxx traite les classes C++ comme des types opaques manipulables uniquement via UniquePtr. Pour les structs simples sans constructeurs/destructeurs non triviaux (les POD — Plain Old Data), on peut demander un passage par valeur :
// C++
struct Point {
double x;
double y;
};include_cpp! {
#include "geometry.h"
safety!(unsafe_ffi)
generate_pod!("Point") // Génère un type Rust copiable, par valeur
}
fn main() {
let p = ffi::Point { x: 1.0, y: 2.0 }; // Sur la pile, pas de UniquePtr
println!("({}, {})", p.x, p.y);
}generate_pod! instruite autocxx de vérifier que le type est effectivement trivial (constructeur/destructeur triviaux, pas de membres virtuels) et de le rendre disponible par valeur en Rust avec #[repr(C)]. Si le type n'est pas trivial, la compilation échoue avec un message explicatif.
Quand generate_all! inclut des types problématiques, on peut les exclure :
include_cpp! {
#include "big_library.h"
safety!(unsafe_ffi)
generate_all!()
block!("InternalHelper") // Exclure ce type
block!("deprecated_function") // Exclure cette fonction
}autocxx ne supporte pas les templates directement, mais il peut générer des bindings pour des instanciations concrètes si un typedef existe côté C++ :
// C++ — Typedef d'une instanciation concrète
using IntVector = std::vector<int>;
using StringMap = std::map<std::string, std::string>; include_cpp! {
#include "types.h"
safety!(unsafe_ffi)
concrete!("IntVector", IntVector)
concrete!("StringMap", StringMap)
}Sans typedef, la solution est de créer un header adaptateur côté C++ qui fournit les typedefs nécessaires.
autocxx gère std::string de manière transparente dans les signatures. Côté Rust, il apparaît comme cxx::UniquePtr<cxx::CxxString> (retour par valeur) ou &cxx::CxxString (référence) :
// C++
class Config {
public:
std::string name() const;
void set_name(const std::string& name);
};let cfg = ffi::Config::new().within_unique_ptr();
// name() retourne une UniquePtr<CxxString>
let name = cfg.name();
println!("Name: {}", name.to_str().unwrap());
// set_name() prend une référence — on crée une CxxString temporaire
let new_name = autocxx::cxx::let_cxx_string!(n = "updated");
cfg.pin_mut().set_name(&n); Les fonctions C++ qui retournent un std::unique_ptr<T> sont mappées naturellement vers cxx::UniquePtr<T> en Rust :
// C++
std::unique_ptr<Engine> create_engine(const std::string& config);let engine = ffi::create_engine(&config); // UniquePtr<Engine>
engine.compute();
// Droppé automatiquement → destructeur C++ appeléLes paramètres const std::vector<T>& sont consommables depuis Rust. Pour les types supportés, autocxx convertit automatiquement :
// C++
void process_data(const std::vector<double>& data);
std::vector<int32_t> get_results(); // Appeler avec un CxxVector
let results = ffi::get_results();
for i in 0..results.len() {
println!("{}", results.as_slice().unwrap()[i]);
}Supporté de manière similaire à unique_ptr, via cxx::SharedPtr<T>.
Certains types STL n'ont pas de mapping direct dans autocxx en 2026 :
std::optional<T>— nécessite un wrapper manuel ou un passage via pointeur.std::variant<T...>— trop complexe pour la génération automatique.std::function<T>— pas de support direct ; utiliser des pointeurs de fonctions C.std::tuple<T...>— utiliser des structs nommées à la place.std::map<K, V>/std::unordered_map<K, V>— support limité, souvent via typedef etconcrete!.
Pour ces types, la stratégie est de créer un header adaptateur côté C++ qui convertit les types non supportés en types supportés :
// adapter.h — Couche d'adaptation pour autocxx
#pragma once
#include "real_api.h"
#include <cstdint>
// Convertir std::optional<double> → double + bool
struct OptionalDouble {
double value;
bool has_value;
};
inline OptionalDouble get_threshold_adapted() {
auto opt = get_threshold(); // Retourne std::optional<double>
if (opt.has_value()) {
return {*opt, true};
}
return {0.0, false};
}Le code généré par autocxx est fonctionnel mais pas toujours idiomatique. Les UniquePtr, les .pin_mut(), les constructeurs numérotés (new1, new2), et les types CxxString créent une interface qui ne ressemble pas à du Rust naturel.
La pratique recommandée est de créer une couche Rust idiomatique au-dessus des bindings bruts :
// src/geometry.rs — Wrapper Rust idiomatique
use cxx::UniquePtr;
/// Rectangle avec une API Rust naturelle.
pub struct Rectangle {
inner: UniquePtr<ffi::Rectangle>,
}
impl Rectangle {
/// Crée un rectangle avec les dimensions données.
pub fn new(width: f64, height: f64) -> Self {
Self {
inner: ffi::Rectangle::new1(width, height).within_unique_ptr(),
}
}
/// Crée un carré unitaire.
pub fn unit_square() -> Self {
Self {
inner: ffi::Rectangle::unit_square().within_unique_ptr(),
}
}
pub fn area(&self) -> f64 {
self.inner.area()
}
pub fn perimeter(&self) -> f64 {
self.inner.perimeter()
}
pub fn scale(&mut self, factor: f64) {
self.inner.pin_mut().scale(factor);
}
pub fn width(&self) -> f64 {
self.inner.width()
}
pub fn height(&self) -> f64 {
self.inner.height()
}
pub fn set_dimensions(&mut self, width: f64, height: f64) {
self.inner.pin_mut().set_dimensions(width, height);
}
/// Retourne une description lisible.
pub fn describe(&self) -> String {
self.inner.describe()
.to_str()
.unwrap_or("invalid UTF-8")
.to_owned()
}
}
impl std::fmt::Display for Rectangle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Rectangle({}x{}, area={})", self.width(), self.height(), self.area())
}
}Le code applicatif utilise la couche idiomatique, qui cache complètement les détails de FFI :
// src/main.rs
mod geometry;
use geometry::Rectangle;
fn main() {
let mut rect = Rectangle::new(3.0, 4.0);
println!("{}", rect); // Rectangle(3x4, area=12)
rect.scale(2.0);
println!("After scale: {}", rect); // Rectangle(6x8, area=48)
println!("Description: {}", rect.describe());
}Ce pattern de wrapper est la norme dans les projets industriels. La couche autocxx (ou cxx) est considérée comme de la plomberie interne — seule la couche idiomatique est exposée aux consommateurs Rust.
Code applicatif Rust (safe, idiomatique)
│
▼
Wrappers Rust idiomatiques (safe, gère UniquePtr/Pin)
│
▼
Bindings autocxx/cxx (unsafe encapsulé, généré)
│
▼
Code C++ métier (inchangé)
autocxx et cxx ne sont pas mutuellement exclusifs. Dans le même projet, on peut utiliser :
- autocxx pour les parties de l'API C++ qui se prêtent bien à la génération automatique (classes simples, fonctions avec des types standard).
cxxpour les parties qui nécessitent un contrôle plus fin (callbacks complexes, types personnalisés, direction Rust → C++).
Les deux partagent les mêmes types de pont (UniquePtr, CxxString, etc.) et coexistent dans le même binaire.
// src/main.rs
// ── Bindings autocxx (C++ → Rust automatique) ──────────────
use autocxx::prelude::*;
include_cpp! {
#include "geometry.h"
safety!(unsafe_ffi)
generate!("Rectangle")
generate!("Circle")
}
// ── Bridge cxx manuel (Rust → C++ et cas spéciaux) ─────────
#[cxx::bridge]
mod manual_ffi {
extern "Rust" {
type RustValidator;
fn create_validator() -> Box<RustValidator>;
fn validate(self: &RustValidator, area: f64) -> bool;
}
unsafe extern "C++" {
include!("geometry.h");
// Fonction avec un callback — plus facile à exprimer manuellement
fn register_on_resize(callback: fn(f64, f64));
}
}
// Implémentation du type Rust exposé vers C++
pub struct RustValidator {
min_area: f64,
}
fn create_validator() -> Box<RustValidator> {
Box::new(RustValidator { min_area: 1.0 })
}
impl RustValidator {
// Exposé via le bridge cxx
}
fn validate(v: &RustValidator, area: f64) -> bool {
area >= v.min_area
}| Situation | Outil recommandé |
|---|---|
| Consommer une API C++ existante (lecture) | autocxx |
| Exposer des types Rust vers C++ | cxx |
| Callbacks et pointeurs de fonctions | cxx |
| Types STL non supportés par autocxx | cxx avec adaptateur |
| Contrôle fin sur la sémantique de propriété | cxx |
| API C++ très large, exploration initiale | autocxx (éventuellement avec generate_all!) |
| Interface stabilisée, taille modérée | cxx |
autocxx produit des messages d'erreur pendant la phase de build (dans build.rs). L'ajout de miette avec la feature fancy dans les dépendances de build améliore considérablement la lisibilité :
[build-dependencies]
miette = { version = "7", features = ["fancy"] }"Unable to generate bindings for type X" — Le type utilise une fonctionnalité C++ non supportée par autocxx (templates non instanciés, héritage multiple complexe, types internes du compilateur). Solutions : utiliser block!("X") pour l'exclure, ou créer un header adaptateur qui expose une API simplifiée.
"No such type: X" — Le type n'est pas trouvé dans les headers inclus. Vérifier les chemins d'include dans Builder::new et la présence du #include dans le bloc include_cpp!.
"Type X is not POD" — generate_pod!("X") a été utilisé sur un type non trivial (destructeur non trivial, constructeur de copie, vtable). Solutions : utiliser generate! au lieu de generate_pod!, ou vérifier que le type est réellement POD.
Temps de compilation très longs — autocxx invoque libclang à chaque build pour parser les headers. Pour les headers volumineux, cela peut prendre plusieurs secondes. Solutions : réduire le nombre de headers inclus, utiliser generate! ciblé plutôt que generate_all!, et s'appuyer sur le cache de compilation (cargo recompile uniquement si les fichiers listés dans rerun-if-changed ont changé).
Conflit avec les mots réservés Rust — Un identifiant C++ comme type, match, impl, fn est un mot réservé en Rust. autocxx renomme automatiquement ces identifiants avec un suffixe _. Si le renommage est problématique, le wrapper idiomatique (section 43.3.3.7) peut fournir des noms appropriés.
Pour comprendre ce que autocxx génère et diagnostiquer les problèmes, on peut afficher le code généré :
# Afficher le code Rust généré
AUTOCXX_RS=output.rs cargo build 2>/dev/null
cat output.rs
# Ou via la variable d'environnement dans build.rs
# env::set_var("AUTOCXX_RS", "generated.rs");Le fichier généré montre les déclarations extern "C", les wrappers de méthodes, et les implémentations de traits — utile pour comprendre les signatures exactes et les types attendus.
Templates non instanciés. autocxx ne peut pas générer de bindings pour template<typename T> class Container. Il faut un typedef concret côté C++ (using IntContainer = Container<int>) et utiliser concrete!.
Héritage virtuel et hiérarchies profondes. autocxx supporte l'héritage simple basique (accès aux méthodes héritées), mais les hiérarchies complexes avec héritage virtuel, héritage multiple ou casting polymorphique ne sont pas gérés.
Surcharge d'opérateurs. Les opérateurs C++ (operator+, operator==, etc.) ne sont pas exposés automatiquement. Il faut écrire des fonctions wrapper C++ nommées ou utiliser le wrapper Rust idiomatique pour implémenter les traits Rust correspondants.
Macros C++ et code conditionnel. Le préprocesseur est évalué par libclang, mais les macros qui définissent des types ou des fonctions ne sont pas toujours correctement interprétées. Les headers fortement macroïsés peuvent nécessiter un header adaptateur.
Exceptions C++ non gérées uniformément. autocxx ne génère pas automatiquement de conversion exception → Result pour toutes les fonctions (contrairement à cxx qui le permet par déclaration). Les exceptions qui traversent la frontière causent un abort. Pour les fonctions susceptibles de lever des exceptions, un wrapper C++ avec try/catch ou l'utilisation de cxx est nécessaire.
autocxx est activement développé et utilisé chez Google (notamment dans le projet Chromium pour des explorations d'interopérabilité). En 2026, il est considéré comme production-ready pour les cas d'usage documentés, mais avec des limitations connues sur les fonctionnalités C++ avancées. L'API peut encore évoluer entre versions mineures — épingler la version dans Cargo.toml est recommandé.
La documentation officielle (https://google.github.io/autocxx/) est la référence pour l'état actuel du support et les workarounds connus.
| FFI manuelle | cxx | autocxx | |
|---|---|---|---|
| Effort initial | Élevé | Modéré | Faible |
| Sécurité | Faible | Élevée | Élevée |
| Couverture C++ | Complète (via C) | Sous-ensemble | Large (avec limites) |
| Direction | Bidirectionnel | Bidirectionnel | Principalement C++ → Rust |
| Types riches | Non (types C) | Oui | Oui |
| Maintenance | Lourde | Modérée | Légère |
| Cas d'usage | Petites interfaces, fallback | Interfaces définies, bidirectionnel | Grandes API existantes |
La recommandation pratique pour un projet en 2026 :
- Commencer avec autocxx pour le prototypage et l'accès rapide à l'API C++ existante.
- Stabiliser avec cxx les interfaces critiques qui nécessitent un contrôle fin ou une communication bidirectionnelle.
- Retomber sur la FFI manuelle uniquement pour les cas exceptionnels que ni
cxxni autocxx ne couvrent. - Toujours encapsuler les bindings bruts dans des wrappers Rust idiomatiques.
📎 La section suivante (43.3.4) aborde la question stratégique : quand et comment introduire Rust dans une base de code C++ existante — la stratégie de migration progressive, l'organisation du build system mixte, et la gestion du changement dans les équipes.
⏭️ Stratégie de migration progressive : quand et comment introduire Rust