| name | cpp-to-rust-port |
| description | Use for systematic migration of C++ libraries (full or partial) to production-grade Rust. Trigger when users ask to port a C/C++ codebase, rewrite selected modules, replace pointer-heavy/OOP-heavy designs with ownership-based Rust APIs, preserve behavior and performance, design incremental FFI bridges, set up mixed C++/Rust build systems, port concurrent or template-heavy code, or enforce advanced semantic Rust patterns (traits, enums, typestate, error taxonomy, unsafe encapsulation, and concurrency correctness). |
C++ to Rust Porting Skill
Execute C++ to Rust migrations with a parity-first, safety-first workflow. Port only the required surface area unless the user explicitly asks for a full rewrite.
Choose Porting Scope First
- Identify the minimal required feature set.
- Freeze behavior with tests before rewriting code.
- Prefer incremental replacement with stable interfaces.
- Keep C++ in place for low-priority paths until Rust equivalents are validated.
- Never attempt a big-bang rewrite. Every successful large-scale migration (Google Android, Cloudflare Pingora, Meta mobile, ClickHouse) used incremental replacement.
Load porting-playbook.md for the full phase-by-phase workflow.
Build a Parity Harness Before Porting
- Add characterization tests around public APIs and important edge cases.
- Add corpus/golden tests for binary formats and protocol behavior.
- Add benchmarks for throughput, latency, allocations, and memory.
- Add differential tests that run both C++ and Rust implementations on the same inputs and compare outputs.
- Treat this harness as the migration gate; avoid semantic rewrites without baseline evidence.
Load perf-and-parity.md for test gates, differential testing strategy, and sanitizer integration.
Set Up the Mixed Build System
- Pick one build system as the outer orchestrator (CMake, Bazel, or Cargo).
- Integrate the other language's toolchain via plugin or subprocess (Corrosion for CMake, rules_rust for Bazel, cc/cmake crates for Cargo-primary).
- Match debug/release profiles, sanitizer flags, and CRT linking modes across both languages.
- Pin Rust toolchain version in
rust-toolchain.toml and commit Cargo.lock.
- Audit transitive dependencies with
cargo-audit and cargo-deny before accepting new crates.
Load build-system-integration.md for CMake, Bazel, and Cargo integration recipes.
Design Idiomatic Rust APIs, Not Line-by-Line Translations
- Model invalid states as unrepresentable states using enums/newtypes.
- Replace inheritance trees with traits and composition.
- Replace nullable/raw ownership contracts with
Option<T>, Result<T, E>, and explicit lifetimes.
- Separate zero-copy read paths from owning transform paths.
- Prefer domain errors over string errors.
- Replace C++ move constructors with Rust's default move semantics (remove all
std::move() calls).
- Replace
dynamic_cast/RTTI with enum dispatch or trait objects (avoid Any unless genuinely needed).
- Replace C++ preprocessor macros with
const, generic functions, or #[cfg()] attributes before reaching for macro_rules!.
Load cpp-rust-pattern-map.md for basic construct mapping.
Load advanced-pattern-map.md for templates, RTTI, macros, move semantics, and anti-patterns.
Port Concurrency Carefully
- Redesign around Rust's
Send/Sync ownership model rather than mimicking C++ shared-state patterns.
- Prefer channels (
crossbeam::channel, mpsc) over Arc<Mutex<T>> for thread communication.
- Map C++ thread pools to Rayon (data-parallel) or Crossbeam (scoped threads).
- Map C++20 coroutines to Rust
async/await with a single runtime (Tokio by default). Do not mix runtimes.
- Never hold a
std::sync::MutexGuard across an .await point. Use tokio::sync::Mutex in async code.
- Do not expose Rust futures across FFI. Use blocking wrappers or callback APIs at the boundary.
Load concurrency-porting.md for the full concurrency pattern map.
Port in Vertical Slices
- Port one subsystem end-to-end (data model, parser/logic, API boundary, tests).
- Keep each slice releasable and benchmarked.
- Avoid giant single-PR rewrites.
- Remove dead C++ code only after Rust parity gates pass.
Contain unsafe and FFI
- Keep
unsafe localized behind safe Rust APIs.
- Document invariants above every
unsafe block with // SAFETY: comments.
- Catch panics at every FFI entry point with
std::panic::catch_unwind. Unwinding across FFI is undefined behavior.
- Define ownership transfer explicitly for every cross-language pointer.
- Use
#[repr(C)] on all types shared across FFI. Never pass Vec, String, or Box directly.
- Handle platform-specific linking (stdc++ on Linux, c++ on macOS, MSVCRT on Windows).
- Use FFI only as a transition seam, not a permanent architecture default.
Load ffi-and-unsafe-boundaries.md for boundary rules, ABI details, and platform linking.
Avoid Known Anti-Patterns
- Do not wrap everything in
Arc<Mutex<T>> to silence the borrow checker. Redesign ownership instead.
- Do not use
Rc<RefCell<T>> as a crutch. It moves safety checks from compile time to runtime panics.
- Do not translate C++ class hierarchies 1:1. Flatten to enums or composition.
- Do not clone excessively. Use borrows,
Cow, and slices at API boundaries.
- Do not ignore dependency count. Audit transitive dependencies before accepting new crates.
Load advanced-pattern-map.md for the full anti-patterns catalog.
Avoid Performance Traps
- Always benchmark in
--release mode. Debug Rust is 10-100x slower due to unoptimized iterators.
- Watch for excessive
.clone() calls โ each one is a hidden heap allocation.
- Box large enum variants to prevent size bloat across all variants.
- Use iterators instead of indexed loops to avoid bounds-checking overhead.
- Batch FFI calls to avoid per-call overhead (4x for simple types, negligible for bulk data).
- Match global allocator to the C++ version if allocation behavior matters (jemalloc, tcmalloc).
- Validate UTF-8 once at the FFI boundary, not on every string conversion.
Load performance-traps.md for the full performance trap catalog.
Apply Advanced Semantic Rust Patterns
- Use typestate or sealed constructors to enforce protocol/state transitions.
- Use trait-based extension points instead of virtual class hierarchies.
- Use enum-driven dispatch for finite strategy sets.
- Use
Arc, channels, and task boundaries for concurrency with explicit ownership.
- Use
Cow, slices, and iterator pipelines for low-allocation data flow.
- Use
#[non_exhaustive] and forward-compatible error enums for stable public APIs.
Define Completion Gates
- Functional parity: all characterization, differential, and new Rust tests pass.
- Safety parity:
unsafe blocks are minimal, documented, and reviewed. Miri passes on unsafe code.
- Performance parity: no regressions beyond agreed thresholds. Benchmarked in release mode with Criterion.
- Operability parity: logging, metrics, and error observability preserved.
- Maintenance parity: public Rust APIs are documented and idiomatic.
- Supply chain: dependencies audited with
cargo-audit and cargo-deny.
- Build system: mixed build compiles cleanly with matching sanitizer and profile flags.
Use References Intentionally
- Load porting-playbook.md for migration sequencing and deliverables.
- Load cpp-rust-pattern-map.md for basic construct-level translation.
- Load advanced-pattern-map.md for templates, RTTI, macros, move semantics, and anti-patterns.
- Load ffi-and-unsafe-boundaries.md for FFI safety, ABI, and platform linking.
- Load concurrency-porting.md for thread, async, and atomics porting.
- Load build-system-integration.md for CMake, Bazel, and Cargo integration.
- Load performance-traps.md for Rust-specific performance pitfalls.
- Load perf-and-parity.md for test/bench gating and differential testing.
- Load sources.md for canonical language, tooling, and case study references.