Rewriting a Kotlin framework in Rust - code organization

Hi!

I’ve been developing a Kotlin framework that’s grown to about 80k lines of code, and I’m planning to rewrite it in Rust.

I’m new to Rust, so my plan is to start with proof-of-concept implementations, experimenting with different approaches, and creating a clear rewrite plan before committing to the full migration.

My first question is about source code organization. The current project has around 20 non-trivial subprojects, each containing many packages.

What would be a good approach for organizing a Rust project of this size?

You can check out the current project here (still in development, not production-ready):

Thanks in advance for any advice or opinions!

On the code organization part, you might want to have a look at the cargo workspaces feature.

Thanks, I did that but I haven't found organization advice for larger projects. Right now the project has 2200+ Kotlin source files which I have to organize into some structure I can properly manage. Using workspaces is the way to go of course, but I'm not sure how to approach what's inside each subproject.

My advice would be to first get a good feeling of both the language and how cargo works. Maybe with a little project or a mock-up, or if your project allows it, a small part that could live on its own.

You can still change the organization of the crates and packages later without too much trouble, if you see it becomes necessary.

I don't know how comfortable with Rust you already are, but one big difference with Kotlin is the absence of classes (more specifically, inheritance). You shouldn't try to find a one-to-one substitute in Rust for it, but instead use the language's type system to its full advantage. Because of that, you may have to re-organize the structure somewhat, though I expect it'll be more at the library level than at the top structure level.

1 Like

Thanks. I've been reading up on Rust and already tried a few small things. Actually, one of the reasons why I want to migrate is the lack of classes/inheritance, which I think will be quite beneficial at the end.

I'm quite sure I'll have to reorganize many things, but that's fine. I never had expectations to do this fast. :slight_smile:

2 Likes

The reference sharing system (borrow checker) is quite beneficial, too, even if it's not always obvious how to solve problems at first.

I'm coming from Kotlin, too, or at least that's the main language I was using before Rust, and like you, I also had the desire to rewrite a project. I can see the problems in my original Kotlin code much more clearly, now.

After another quick glance at your project, I don't have the impression the overall structure must really change. You'll have the choice of where you place each package boundary, but it seems that every gradle project has enough code under to justify its own package in Rust.

I haven't done such huge projects yet, but I think it's not possible to define sub-workspaces (someone please correct me if I'm wrong). So you could either define all the packages in a workspace at the top level:

[workspace]
members = ["core/core-build", "core/core-core", "core/core-ui", ...]

(and likely no [package] definition at that level).

Or you could split your project in "core", "lib", etc., which could be more manageable. I've only had a quick glance, so I may have missed something that would prevent a split.

You don't have to define a top-level workspace, but it's usually more convenient (see some of the reasons here). I don't know if there's a way to deal with 2 levels other than flattening everything, though. Hopefully someone else does. Don't hesitate to look at large Rust projects to see how they're organized.

When you want to publish crates, there are specific constraints, too, so don't do a cargo publish at the top level. But that's for later.

1 Like

If that helps, I just saw a project that used patterns, so that's much easier to write. As the Cargo book says:

The members list also supports globs to match multiple paths, using typical filename glob patterns like * and ?.

So something like this should work:

[workspace]
members = ["core/*", "¨lib/*", ...]

and if a directory corresponding to the pattern isn't a package, like a data directory, you can use (for ex) exclude = ["core/data"].

1 Like

Thank you, this is quite useful. (To be honest, I am sometimes too impatient to read all the long sequences of the documentation. While they are great when I need the details, I prefer shorter examples with bullet point explanations.)

1 Like

To be honest, I know only a small part of the cargo system. It's very flexible, and I'm not sure everything is detailed in the Cargo book. That bit about exclude and the glob patterns is new to me, and I wouldn't have noticed when I saw it if not for your question.

I have also Java/Kotlin background and migrated to Rust recently. I do not use Cargo though, and I reminded on my Java building system, however I rewrote it in Rust. I was skeptical doing the work, but now I am pleasant that I did it. YMMV.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.