I've mostly been putting functions, structs enums and tests in the same file and it's starting to feel like a mess... I am constantly losing track of which function is where and I'm never quite sure if I have code in the right place. I know this is kind of vague, but that's been my general feeling while working in my largest personal project so far. It's essentially a large Algorithm that has many smaller steps that needs to run in a sequence.
I think I might just have too many loose functions and maybe I should use more impl methods and traits? I'm also thinking I should try the builder pattern in a few spots.
Anyone have any guidance on code organization in rust?
At Google, my team had a tried and true policy on organization:
"It doesn't matter how you organize the code, just do it consistently."
Don't sweat the specifics too much, just try to be consistent in how you name and order your functions. That's the magic ingredient that lets you go from one part of the code to another without having to ramp up.
Specifically regarding traits and impls and such, they can be good, but I wouldn't worry if you're not using them. It's good to experiment with new paradigms, but you don't have to. Many successful programs and libraries are written using nothing but functions.
If you're interested in organization, I strongly recommend learning about code smells and refactorings (e.g. refactoring.guru). Also unit testing can lead you toward better organization because it forces you to think about how you'll call the code. Besides that, just have fun!
This is really good to hear, I don't think I'm as far off base as I thought; maybe I've been over thinking it a bit. And thanks for that refactoring resource. I'm very big into making my TS code clean and maintainable. I'm just thrown off a bit with the new paradigm.
The book has pretty good details on this. If you’re looking for how to actually scope modules, do some googling for package or library composition ideas. That’s a pattern that’s consistent across languages.
Functions are fine, don't move to struct impls unless it makes sense (but do if the functions all take the same struct as a param).
You can go pretty far with modules and functions. Group related functions and move them to new modules. You can also hide functions that are only used inside one of the submodules by just not marking them as
pub
.One thing that comes to mind is that if the steps of your algorithm all take and return the same data, you can have a trait that expresses that (possibly one of the
Fn
traits if you're going to just use functions), and you can define and rest each step separately.It's hard to give more concrete advice without knowing more about your project
Normally during a project, I tend to restructure the code quite a bit. First when it is small, I do it like you and have everything in one file, then as it grows I start to split out the things in to multiple files/modules. Then as it grows even further, I create subfolders. Try to define parts of the algorithms and break them out to their own modules. Like if you have a scheduling part, then you move that to
scheduler.rs
. Also, move out special types totypes.rs
, error types toerrors.rs
to keep the area with the actual algorithms more clear.So, that the code feels like a mess as it grows is just a normal thing. And often, it is not worth trying to plan that much ahead since it is very difficult to predict the needs.
But for a REST server I have something like this
src/main.rs src/types.rs src/api/v1/mod.rs src/api/v1/errors.rs src/api/v2/mod.rs src/api/v2/errors.rs src/api/v2/types.rs src/tests/v1.rs src/tests/v2.rs
But the before the v2 version of the api, there was just a src/api.rs, src/errors.rs . So, I think the key is to not be afrad to shuffle code around and restructure it as you need. And it will not always be good, but then you just do it again. One of the things with a very strict language like Rust is that you can shuffle it around, and rewrite it without a big risk of adding hidden bugs.
Also, move out special types to types.rs, error types to errors.rs to keep the area with the actual algorithms more clear.
Ok this is totally something my code base needs. Very actionable feedback.
And yeah that's one of the things I love about rust; it will tell me everywhere things are out of wack. It's such a different experience from back when I had large JavaScript code bases. Make changes and pray lol.
I am constantly losing track of which function is where and I’m never quite sure if I have code in the right place.
That's not a Rust concern but a software architecture concern. The software architecture pattern you follow determines how the project is structured taking into account what requirements it needs to comply to address common requirements, such as ensuring that things that change a lot will be easier to change and things that don't change won't get in the way.
I recommend you read up on topics such as layered architecture, hexagonal architecture/ports and adapters, clean architecture, etc.