Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.
Citrus: Convert C to Rust
This is a tool that helps convert C programs to Rust programs. It transforms C syntax to Rust syntax, but ignores C semantics.
The generated programs will not run, and may even not compile. However, the tool produces readable source code that can be manually refactored into a Rust program.
Note: This is a very early version. There are still obvious bugs and key language features missing. Please contribute bug reports with test cases and, if you can, fixes!
Example
void
unsafe extern "C"
Installation
See releases for binary downloads.
Usage
Usage: citrus <file> [<compiler args…>]
The typical workflow is:
- Clean up the C code — bad C code makes even worse Rust code.
- Convert C syntax to Rust syntax (this one is automated by Citrus!).
- Keep rewriting C-isms into Rust-isms until it compiles (some easy translations are also done by Citrus).
- Refactor into idiomatic Rust code.
C is very weird from Rust perspective. The generated code will be very un-Rust-like. Please don't judge Rust by this :)
Preparing C code for conversion
-
Use
size_t
for all lenghts and array indexing (orssize_t
/ptrdiff_t
if you need negative values). Rust is super picky about this. -
Change as much as you can to
const
: variables, function arguments, globals. In Rust things are immutable by default.- Don't reuse variables, e.g. instead of one
i
reused throughout a function, (re)define it for each loop. - If you get "discards qualifiers in nested pointer types" error, it's not you, it's a "bug" in the C language.
- Don't reuse variables, e.g. instead of one
-
Minimize use of macros. Macros are expanded during conversion, so their high-level syntax is lost.
- Change global constants from
#define
toenum
orconst
global variables. - Change function-like macros to
inline
functions. For conversion you may even want to undefineassert
,MAX
, andoffsetof
. - If you use macros to generate several versions of a function (such as
func_int
,func_float
), keep just one version with a unique typedef for the type. You'll be able to replace the typedefed name with a generic parameter later.
- Change global constants from
-
Replace
int
andlong
with types of specific size, such as<stdint.h>
'sint32_t
,int64_t
orsize_t
.- Use
bool
from<stdbool.h>
- Use
signed char
only when you really mean to use it for negative numbers. unsigned char
,short
,float
,double
andlong long
can be left as-is.
- Use
-
In function arguments use
arr[]
for arrays, and*ptr
for exactly one element.- Prefer array indexing over pointer arithmetic (
arr[i]
yes,ptr+i
no). - Bonus points for
f(size_t length, arr[static length])
(yes, it's a valid C syntax).
- Prefer array indexing over pointer arithmetic (
-
Add
__attribute__((nonnull))
to functions that should not be called withNULL
arguments. -
Change all internal functions to
static
. Syntax of extern C functions will become noisy. -
Change
for
loops to be in formatfor(size_t i = start; i < end; i++)
.- if you can't, then use
while
instead (but avoiddo..while
).
- if you can't, then use
-
Don't use fall-through tricks in
switch
.- Multiple cases for the same block of code (
case 1: case 2: … break;
) are OK.
- Multiple cases for the same block of code (
-
Don't use
var++
in expressions. Use++var
or put it on its own line. Rust only allowsvar += 1;
-
Remove all
goto
and its labels. -
Remove "clever" micro-optimizations. They are really painful to convert, and most end up being not applicable.
- Avoid tricks with pointers, unions, type casting, or reuse same chunk of memory for different purposes.
- Consider returning things by value instead of taking in-out pointers, e.g. if you need to return multiple things, return a small struct by value instead.
-
Having tests helps. Not only unit tests, but also a high-level test such as a known-good whole program output.
Cleanup post conversion
- Verify code side-by-side to ensure nothing is missing or mistranslated.
- Replace fixed-size arrays with slices or
Vec
. Rust's fixed-size arrays are PITA.
Building the Citrus tool from scratch
Because if the C3 dependency it requires exactly LLVM 4.0 and a corresponding static Clang library (libclang.a
+ headers). You may need to build Clang from source for this (sorry). The stable C API of clang is not sufficient for the task, so Citrus has to use a fragile C++ clang API, which is not guaranteed to be compatible with anything.
Build/install LLVM 4 and Clang. Set variables to LLVM and Clang locations:
# Must have 'libclang.a'
# Path straight to the 'llvm-config' executable
# Should contain 'clang' and 'clang-c' sub-directories