Skip to content

Instantly share code, notes, and snippets.

@bvssvni
Last active November 11, 2017 19:32
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bvssvni/9343353 to your computer and use it in GitHub Desktop.
Save bvssvni/9343353 to your computer and use it in GitHub Desktop.
How To Organize Code in Rust per Object or Function
//! rustc 0.10-pre (ee8f45e 2014-02-18 13:41:49 -0800)
//!
//! Before you bite my head off, this is NOT a recommend way of organizing code!
//! It is only a demonstration what you can do in Rust! (wish I put this comment in earlier)
//! My intention with this example is to show new people the features of the module system.
//!
//! The module system in Rust gives you precise control
//! over the interface when writing libraries.
//! You can write the layout independent of how you organize the code!
//! In this example two different conventions are explored.
//!
//!
//! FILE PER OBJECT (OBJECT ORIENTED STYLE)
//!
//! Many programmers are accustomed to organize code per object.
//! This is common in languages like C++, C# and Java.
//! In this example we demonstrate this:
//!
//! A struct 'Message' is located in 'Message.rs'.
//!
//!
//! FILE PER FUNCTION (FUNCTIONAL STYLE)
//!
//! In large codebases, navigating to the right place can take time.
//! To make this easier one might put each function in its own file (grouped by folders of course).
//! This also helps avoiding writing duplicate code for same functionality.
//! It is a pragmatic way that works on business logic bloat, for example in C#.
//!
//! A function 'print_message' is located in 'print_message.rs'.
//!
//!
//!
//! WHY IS RUST DIFFERENT?
//!
//! Rust organizes code by modules by default.
//! The modules are tied to the file system!
//! You do not have to declare a module per file.
//!
//! mod test; // looks up 'test.rs'.
//!
//! A 'use' statement brings functions and type into scope.
//! In this example you see a 'use' declared locally in a function.
//!
//! use tricky::stuff::print_message;
//!
//! print_message(msg);
//!
//! It tells you how where the declaration is by looking at the code.
//! By default it would be a 'stuff.rs' file in a 'tricky' folder.
//!
//!
//! In this example we will override the default behavior in Rust.
/// This module declares the layout of the library.
/// This is how code see the library externally and internally.
/// For this reason, it must be declared before any modules!
pub mod tricky {
pub mod stuff {
// The names appear twice on the right side,
// one for the file and a second time for the declaration.
// Change the name on the left side as you like!
pub use print_message = print_message::print_message;
pub use Message = Message::Message;
// Also, if you forget to make a function 'pub'
// the compiler will tell you!
}
}
// mod Message; // Uncomment if using another file.
mod Message {
// The content of this module can be put in 'Message.rs'.
// You do not declare modules by file.
pub struct Message {
msg: ~str
}
impl Message {
/// Creates a new Message.
pub fn new(msg: ~str) -> Message {
Message { msg: msg }
}
pub fn print(&self) {
// We can now use the functions
// with the layout we decided.
use tricky::stuff::print_message;
print_message(self.msg);
}
}
}
// mod print_message; // Uncomment if using another file.
mod print_message {
// This content of this module can be put in 'print_message.rs'.
// You do not declare module per file.
/// Is called by Message.print()
pub fn print_message(msg: &str) {
println!("{}", msg);
}
}
fn main() {
use tricky::stuff::Message;
let msg = Message::new(~"Hello");
msg.print();
// You can also call per file::function internally.
print_message::print_message("hi!");
// Outpout:
// Hello
// hi!
}
@dobkeratops
Copy link

i would be curious to know other peoples answers to this too..
my ideal is to split modules to minimize the dependancies in each module, which is why i dont like the OOP idea much, which tends to increase coupling.
An 'class' might seem natural to have a rendering method with graphics dependancies and update method with physics dependancies, but those functions (impls) can go in seprate modules and hence seperate files, hence you can decouple testing and changes.
However would one file per function be going too far dividing things up usually?

if one 'class' (struct/impl pair) doesn't have many dependancies i think i would stick to one file for that

@bvssvni
Copy link
Author

bvssvni commented Mar 4, 2014

The whole purpose here is to demonstrate what you can do with the module system, not to give a recommendation of how to organize the code. I just took two ways to organize code in large project in other languages and showed how to do the same in Rust. Personally I prefer the way Rust does it by default, because how the type system works and integration with rustdoc.

Breaking up one file per function is useful when you have application level abstractions, such that one function does one user action on the whole application state. This is 'monkey" programming because these functions are there just to make things work. It makes it easier to navigate the specific part of the code that does the business logic, without having to make complex decisions about the architecture. It is just a pragmatic way of doing it and surely it is not the best way for all cases.

I have not written any large Rust application yet, so I am eager to find out how other library writers organize their code in the community. When it comes to designing purpose specific libraries I put stuff together by functionality and try to minimize the external surface of the API.

Thanks for constructive feedback!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment