I am creating my first project and I'm a little lost on how to properly structure all the files.
The file structure I currently have works, but it somehow feels wrong and I'm not sure if that's the way it is supposed to be, or if I'm doing something wrong.
The files are as follows: (not showing all code for all files to keep it short)
main.rs:
use projectname::graph::graph::Graph;
lib.rs:
pub mod graph;
graph.rs:
pub mod edge;
pub mod graph;
pub mod node;
graph/edge.rs
graph/node.rs
graph/graph.rs:
use super::edge::Edge;
use super::node::Node;
graph/routing.rs:
pub mod routing;
graph/routing/routing.rs:
use super::super::edge::Edge;
use super::super::graph::Graph;
...
My main questions are is that it feels weird to have a file that just contains some pub mod statements...
And secondly the use of super::super in graph/routing/routing.rs feels wrong
and Thirdly in main.rs it feels weird that I have to use graph::graph instead of just graph.
Am I doing something wrong or is this how it is supposed to be?
graph/graph.rs is generally discouraged (I think clippy might warn about it by default). It's generally preferred to put the code you have in graph/graph.rs in graph.rs instead.
Otherwise it can get a little confusing which is the file defining the outer graph module.
For the most part though I agree that your structure looks fine. One thing to keep in mind is that having types in a lot of different modules can be confusing for users of a library. When there isn't an obvious reason to keep them separate, it can be better to re-export most of your types and functions at the top level[1] instead of making all the intermediate modules public.
For example, in graph.rs I would probably do
pub use edge::Edge;
pub use node::Node;
pub use graph::Graph;
Confusing, or even just annoyingly verbose. In Rust, it is common but not universal style to use module/crate imports instead of individual symbols â that is, like this:
use std::fmt;
impl fmt::Debug for MyType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
In my opinion, you should design your library so that it is comfortable to use that way â not requiring the user to import very many modules to get concise yet meaningful paths at usage sites. This may mean that your public API has significantly fewer public modules than it does private implementation modules, where the public modules might re-export items defined in the private modules.
Also, more broadly, it is not idiomatic Rust to have a module per type. Which isn't to say that you shouldn't; just that you should do it for a reason and not just because it's the default choice. In general, the time when a type has a public module for itself is when it has related items â usually other types that are closely related to it, like iterators. For example, std::collections::hash_map exists because HashMap has many iterators and Entry.