loading...

Learning Rust - Introduction to Cargo and project structure

brunooliveira profile image Bruno Oliveira ・3 min read

Introduction

So far, all the Rust code we have written is self-contained and small enough to fit in a single, isolated source file that can be compiled directly using rustc.
While this is a great way to explore the most basic constructs of a language, or simply to experiment with new concepts "in isolation", there are scenarios that demand some sort of code structure that makes it infeasible to keep a single source file.

We will see how Rust aids us in managing a larger project, how to structure it, and we will learn about Cargo which is Rust's package manager. Cargo downloads your Rust package’s dependencies, compiles your packages, makes distributable packages, and uploads them to crates.io, the Rust community’s package registry.

Installing Rust and Cargo

By navigating to the official website for the language, or, checking the book we can simply follow the instructions there to install both the cargo package manager and Rust, in one go. Under Linux/MacOS, this does the trick:

$ curl https://sh.rustup.rs -sSf | sh

This will install both cargo and rust, and we can start managing our Rust code using cargo. Let's see how to setup a basic program via cargo and how to compile it and run it, and lastly, we will touch upon why we need cargo when working with larger rust projects.

Prepare a binary program using cargo

Rust (well, cargo) makes a distinction between binary programs and libraries.

The default behavior of cargo is to generate a binary program when called with no flags. As such:

cargo new hello_world

will create a new package that will hold a binary program. If we had passed the --lib argument, we'd make a library instead.

Let's see what this generates for us:

$ cd hello_world
$ tree .
.
├── Cargo.toml
└── src
    └── main.rs

1 directory, 2 files

So, we can see one directory and two files have been generated.

The directory name is src and this is a Rust convention to say that, inside this directory we will have Rust application code.

As in many other languages, main.rs is the entry point for a more complex Rust application, and cargo uses these conventions to work.

If we had libraries, or had ran the command with the --lib flag, it would go inside a lib directory, again, by convention.

Exploring Cargo.toml

The other auto-generated file is called Cargo.toml and it's a special file, in the sense that we can see it as a manifest, and it contains all of the metadata that Cargo needs to compile our package.

[package]
name = "hello_world"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"

[dependencies]

We have two main sections in our Cargo file: package and dependencies.

The package section contains information about our project, and it can be seen as simple meta-information, such as, project name, author's names, version, etc. This is auto-generated but can be completed and later maintained by the developer(s).

The dependencies section contains dependencies for our project, which are called crates in Rust terminology.

These can be external crates, retrieved from a central repository such as crates.io or it can be user-defined crates containing specific functions.

Any Rust project created and/or managed via cargo, knows all it needs to, via the Cargo.toml file. This is a dynamic file that grows and changes together with your project, and it has some noteworthy goals as to why it exists in the first place:

  • Introduces two metadata files with various bits of package information.
  • Fetches and builds your package’s dependencies.
  • Invokes rustc or another build tool with the correct parameters to build your package.
  • Introduces conventions to make working with Rust packages easier.

Building and compiling with Cargo

We can use Cargo both as a package manager as well as a build system for our Rust programs, and we can also compile and run our programs using it, which we can and should do.

Simple compile, followed by manual run

Using cargo build in the root directory will compile the project, which can then be run manually by going into the target and debug folder and run the generated executable from there:

$ ./target/debug/hello_world
Hello, world!

All in one go

To do all in one go, we can do:

cargo run

which will compile and then run the program.

Conclusion

Next, we'll see how to create our own modules and structure our project with them.

Discussion

pic
Editor guide
 

A good thing you could mention is Cargo's ability to automatically build a binary for every .rs file Cargo finds in src/bin/.

For example, the following project structure would produce 2 binaries.

$ ls src/bin
daemon.rs
ctl.rs
$ ls target/build/debug
daemon.exe
ctl.exe
 

Thanks will edit it! Merci