| name | rust-migration |
| description | C/C++ to idiomatic Rust conversion patterns and pipeline. Covers pointer translation, memory management, error handling, string conversion, struct patterns, and quality gates. Use when: convert C to Rust, migrate to Rust, C to Rust patterns, Rust migration, rewrite in Rust. |
| license | MIT |
| metadata | {"version":"1.0.0","category":"migration","tags":["rust","c","cpp","migration","conversion","patterns","idioms"]} |
C to Rust Migration Patterns
Pipeline Process
- Analysis — classify difficulty, detect patterns, plan strategy
- Translation — C → safe Rust (LLM, optionally with C2Rust context)
- Quality gate — re-translate if >5 unsafe blocks (temp 0.5)
- Validation — compile + diff test + idiomatic score
- Repair loop — fix errors preserving quality floor (P0)
- Best-version fallback — keep highest-quality version even if doesn't compile (P1)
Memory Management
let arr: Vec<i32> = vec![0; n];
let val = Box::new(42);
let items: Vec<Item> = Vec::new();
v.resize(new_size, 0);
Data Model Migration (learned from cJSON)
enum JsonValue {
Null,
Bool(bool),
Number(f64),
Str(String),
Array(Vec<JsonValue>),
Object(Vec<(String, JsonValue)>),
}
Error Handling
#[derive(Debug, thiserror::Error)]
enum ParseError {
#[error("unexpected token at position {0}")]
UnexpectedToken(usize),
#[error("unterminated string")]
UnterminatedString,
}
fn find(key: &str) -> Option<&JsonValue> { ... }
String Handling
fn process(s: &str) -> usize { s.len() }
fn build() -> String { format!("hello {}", "world") }
fn escape_string(s: &str) -> String { ... }
Function Pointer Migration (learned from genann)
#[derive(Clone, Copy, PartialEq)]
enum ActivationFn { Sigmoid, SigmoidCached, Threshold, Linear }
impl ActivationFn {
fn apply(&self, ann: &Genann, a: f64) -> f64 {
match self {
ActivationFn::Sigmoid => sigmoid(a),
}
}
}
Pointer Patterns
fn sum(data: &[i32]) -> i32 { data.iter().sum() }
fn fill(data: &mut [i32], val: i32) { data.fill(val); }
fn maybe_read(p: Option<&i32>) -> i32 { p.copied().unwrap_or(0) }
fn process<T: AsRef<[u8]>>(data: T) { ... }
Struct Migration
struct Buffer {
data: Vec<u8>,
}
impl Buffer {
fn new(capacity: usize) -> Self {
Self { data: Vec::with_capacity(capacity) }
}
}
Control Flow
State Machine Migration (learned from http-parser)
fn find_crlf(data: &[u8], start: usize) -> Option<usize> {
let mut i = start;
while i + 1 < data.len() {
if data[i] == b'\r' && data[i + 1] == b'\n' { return Some(i); }
i += 1;
}
None
}
use std::cell::RefCell;
thread_local! {
static G_STATE: RefCell<TestState> = RefCell::new(TestState::new());
}
Numeric Formatting (learned from cJSON)
fn format_g(val: f64) -> String {
if val.fract() == 0.0 && val.abs() < 1e15 {
return (val as i64).to_string();
}
let s = format!("{:.17e}", val);
}
Common Pitfalls (from production migrations)
bool vs int: C returns 0/1 as int; Rust bool prints true/false → keep as i32
- Integer overflow: C wraps silently; Rust panics in debug → use
wrapping_add etc.
- Printf format:
%d → {}, %s → {}, %f → {:.6}, %g → custom format_g()
- Signed/unsigned: C implicit conversion; Rust requires explicit
as casts
- Recursive data structures: need
Box<T> for indirection in Rust
- Bit manipulation: same operators but explicit types needed (
u32, i32)
- Use
usize for array dimensions: C uses int for sizes, but Rust indexing requires usize. Using i32 forces as usize on every array access, which penalizes idiomatic score heavily. Prefer usize from the start.
- glibc
srand/rand determinism: C's rand() uses glibc TYPE_3 (degree-31) PRNG. For diff-test to pass, must reimplement the exact PRNG algorithm, not use Rust's rand crate.
a > 0 returns double in C: This is an implicit bool-to-double cast. In Rust: if a > 0.0 { 1.0 } else { 0.0 }
- goto state machines: Don't port byte-by-byte. Rewrite at higher abstraction (line-by-line parsing). 1515 LOC goto → ~200 LOC clean Rust. The
parsed byte count stays correct via position tracking.
- C callback APIs with global state: Use
thread_local! { RefCell<T> } instead of unsafe static mut. Callbacks are plain functions that access the thread_local.
- Frontier target stochasticity: For >2500 LOC with goto/switch state machines, the LLM pipeline converges stochastically. Manual completion is a valid strategy when LLM provides good types+structure but truncates on complex functions.
Powered by Noricum — autonomous C/C++ to Rust migration agent