Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Decy: C/C++ to Rust

Decy transpiles C and C++ source code into safe, idiomatic Rust. Its core challenge is inferring Rust ownership semantics from C pointer patterns and replacing manual memory management with RAII.

Overview

AttributeValue
DirectionC/C++ to Rust
Installcargo install decy
Input.c, .cpp, .h, .hpp files
OutputSafe Rust with ownership and lifetime annotations

Ownership Inference from Pointer Analysis

C uses raw pointers for everything: ownership, borrowing, output parameters, and arrays. Decy analyzes pointer usage patterns to infer the correct Rust ownership model.

C PatternDecy InferenceRust Output
const T* read onlyShared reference&T
T* written throughMutable reference&mut T
T* from malloc, returnedOwned valueBox<T> or T
T* freed in same scopeScoped ownerlet val: T (stack)
T** output parameterReturn value-> T
T* array + lengthSlice&[T] or &mut [T]

Memory Management Translation

Decy replaces malloc/free pairs with Rust RAII, eliminating use-after-free and double-free at compile time.

Buffer* buf = (Buffer*)malloc(sizeof(Buffer));
buf->data = (char*)malloc(size);
free(buf->data);
free(buf);
#![allow(unused)]
fn main() {
// RAII: dropped automatically when buf goes out of scope
let buf = Buffer { data: vec![0u8; size], len: size };
}

Common translations: char* + strlen() becomes String, strdup(s) becomes s.to_string(), strcmp(a,b)==0 becomes a == b, and snprintf becomes format!(...).

FFI Boundary Generation

For gradual migration, Decy generates extern "C" wrappers so existing C code can call the new Rust functions. This allows teams to migrate one file at a time, linking Rust objects into the existing C build system.

#![allow(unused)]
fn main() {
#[no_mangle]
pub extern "C" fn process_buffer(data: *const u8, len: usize) -> i32 {
    let slice = unsafe { std::slice::from_raw_parts(data, len) };
    process_buffer_safe(slice).unwrap_or(-1)
}
}

Pass --ffi to decy transpile to generate these wrappers alongside the safe Rust implementation.

Common C Patterns and Rust Equivalents

C PatternRust Equivalent
for (int i = 0; i < n; i++)for i in 0..n
switch / casematch
typedef structstruct
unionenum with variants
goto cleanup? operator or Drop trait
#define MAX(a,b)std::cmp::max(a, b)
NULL checkOption<T>
errno codesResult<T, E>

CLI Usage

# Transpile a single C file
decy transpile --input parser.c --output parser.rs

# Transpile with FFI wrappers for gradual migration
decy transpile --input lib.c --output lib.rs --ffi

# Transpile a C project directory
decy transpile --input ./c_project --output ./rust_project

# Via Batuta orchestration
batuta transpile --input ./c_project --output ./rust_project

Limitations

  • Inline assembly: Not transpiled; must be replaced manually or wrapped in unsafe
  • Complex macros: Preprocessor macros with side effects require manual review
  • Void pointers: void* used as generic storage needs manual type annotation
  • Bit fields: Struct bit fields are converted to explicit mask operations

Navigate: Table of Contents