30 Days of Rust - Day 19
Tags: rust, learning, devjournal
Published: 2023-05-15 20:29:02

Hi everyone,
I'm writing from Istanbul today. Turkey is gripped by Sunday's presidential elections. Around 88% of eligible voters casted their votes. That level is participation in democracy is unheard of in many western countries! 

Anyway, back to business: today's blog it going to cover how to structure Rust projects. We'll look about how `cargo` provides an opinionated default project setup as well as how to use your code across your project.

## Yesterday's questions answered
*No questions to answer*

## Today's open questions
*No open questions*

## What exactly is a crate again?
Earlier in this blog series, I touched briefly on the subject of crates. At first glance, a *crate* can appear analogous to a *package* (particularly when coming from a Python context). Yet the Rust book offers us a more precise definition of a crate:

> A _crate_ is the smallest amount of code that the Rust compiler considers at a time.

What turns a crate into a package is the presence of a `Cargo.toml` file. Packages can typically be divided into two categories: binary executables or libraries. Some important things to note here are:
- A package can contain *multiple* binary executables
- A package can contain up to *one* library executable
- A package may consist of both binary executables and a library

One interesting design pattern recommended by the book is to include both a library and executable when designing your package. This can help you as a developer to better understand and develop your code's public API: you are both the *publisher* and *consumer* of the API.

You may ask: how does cargo know when you're creating an executable as opposed to a library? The answer lies in the name of the file generated by these two commands:

```shell
cargo new my_executable
ls my_executable/src # => main.rs
```

```shell
cargo new my_lib --lib
ls my_lib/src # => lib.rs
```

Whether your project's **root** is `main.rs` or `lib.rs` is decisive for the compiler. Now let's look at how we can start adding code to our package.

## Packages as file systems
We've seen that packages in Rust can have types of **root** files. The root is the starting point of the compiler's search for your code. From the root, we have to tell the compiler what other code we'll need to compile. This is done through declaring a module using the `mod` keyword:
```rust
// main.rs
mod cool_mod;
fn main() {}
```

As per the Rust book, this will tell the compiler to look in three places:
- Inline (i.e. in the curly brace block which follows)
- In `src/cool_mod.rs`
- Or `src/cool_mod/mod.rs` (this is old-school and should be avoided)

If we want to declare modules in any file other than the root, these can be referred to as *submodules*.

The similarity between the hierarchies of a package in Rust and a file system are pretty clear to see. Remember that a module in Rust, however, does not strictly have to be organised in separate files. For small projects, declaring modules inline in your root may suffice.

## Keep your code private
The code snippet above wouldn't really be of much use to a developer as the module is private. This means the code inside the module cannot be accessed from outside of the scope of that module's block.

By making code private by default, Rust makes the developer actively consider how much of their code should be visible. In theory this should lead to better code design. By the way you can make code public by adding `pub` before its declaration (as a function, module, struct, etc...). Structs declared public will still require fields and methods to also be public if they are to be accessed by an external caller. When an enum is declared public, however, all variants of an enum will become visible.

## Making in-paths
Once public, you can import code using an absolute or relative path. Absolute paths start with `crate::`  whereas a relative path begins with either `self::` , `super::` or the current module's identifier.

Bigger projects may have deeply nested project structures; and if the project uses absolute paths, then you may end up with very long lines merely to call functions declared deep in a submodule in your crate root. The `use` keyword is a solution to this problem. When you `use` a path, the last part of that path will come into scope:

```rust
let result = my_module::my_sub_module::my_sub_sub_module::my_func();
// OR
use my_module::my_sub_module::my_sub_sub_module;
let result = my_sub_sub_module::my_func();
```

Similarly, you can use the as keyword to designate an import alias to something. This is particularly common when importing `Error` or `Result` from different packages:
```rust
use std::fmt::Result; 
use std::io::Result as IoResult;
```

Finally, if you are working with a deeply nested library, you may want to re-export an import path. This can be particularly useful when the library design doesn't match the common understanding of the packages domain:
```rust
pub use my_module::my_sub_module::my_sub_sub_module;

pub fn some_high_level_func() { my_sub_sub_module::some_func(); }
```