Converting C++ to Rust Options

I have decided to convert a relatively small C++ project that I like in Rust. The goal is mostly to better learn the language and get more practice. I quickly realised that I had the option to either closely stick to the original C++ design and have less idiomatic code (which is the easier option) or attempt to redo the logic in a more idiomatic way. What is the general consensus on the topic? Is it dependent on a case by case basis? Or are there some general best practices to approach the problem? Thanks.

2 Likes

One common approach is to start with a direct translation from C++, ensure all tests pass, and then gradually adapt the Rust to make it idiomatic.

4 Likes

I suspect that might run into problems if the C++ uses classes and inheritance.

That is basically the approach I took when wanting a Rust implementation of a C# project. The result works reliably but it is not pretty. I imagine a competent Rustacean would laugh heartily at it. But it works reliably anyway.

Turns out, when I understood it better, that the C# code was just horrible. It managed to have 6 or 7 major classes that were so terribly and cyclicly intertwined that all the code may as well have been in one class.

I regretted that I did not spend time understanding the code properly first and then using what I learned as requirements and hints to build the Rust from scratch.

I would not worry about being "idiomatic" as a Rust beginner. Get something that works first. Fortunately Rust's fanatical checking of types and the borrow checker mean your code will be robust anyway. You might want to rework things to be better Rust as you get more familiar with the language.

10 Likes

If it was a C project, you could use c2rust to get an initial version (unsafe and syntactically ugly) to work on and clean up. In that case you'd end up with likely unidiomatic Rust, but direct translation.

However, if the project is in C++, there's no automated converter like that. Some C++ language features don't have direct equivalents in Rust, so it could be pretty tedious and very unidiomatic to try to translate it directly to Rust.

Especially if the project is small, I'd recommend start again from scratch. Rust likes to have things done the Rust way (e.g. strongly prefers all program data as a tree without backreferences).

1 Like

My approach has been to:

  • get a setup that lets me build and link both Rust and c++ together, generally using the cc crate
  • then find a target c++ file/function to start with
  • add hooks to be able to call into it from Rust
  • Add Rust unit tests to validate that it's independently runnable
  • Port the code as idiomatically add possible to Rust manually, using said tests and adding more as you go to validate the behavior
  • If needed, export the Rust functions back to c++
  • remove/disable the old c++ code
  • Repeat

This is super slow, but you have working code at all points and you end up with a lot more usable Rust code than a machine translation, lots of tests, and you can tackle the code in whatever order makes sense as you try to pull a more rigorous architecture out. The main troubles is you need to be very careful and clever that you don't break Rust's safety requirements, and global state is a beast.

12 Likes

You might be interested in Federico's presentation on converting librsvg to Rust; while librsvg was a C project, not a C++ project, it used GObject and thus had some of the same friction points you'll see (inheritance and the like). If it looks like his experience is of interest, then there's some related blog posts (oldest to newest in the list below):

You may find other posts of interest at Federico's Blog - but those are the ones I could find that I think might interest someone doing a C++ to Rust port.

4 Likes

My first Rust exercise was converting a C# program. This was from a book and it did have classes and interfaces but no OO hierarchies as such. I did initially try to more-or-less copy the C# but found it too difficult, so I ended up just implementing just with structs and no traits. At that point I just wanted to create something that produced the same results.

I then successively improved it over time to be more idiomatically Rust, as my knowledge improved. It probably could be improved further by experienced Rustaceans.

But anyway I second the "get something that works first" approach. I found that in itself to be tough enough!

3 Likes