Introduction to Rust

Building with Rust

Hello, World!

We have already seen how to write “Hello, World” in Rust. In this section, we will demystify all the parts of the Rust syntax relevant to the “Hello, World” code in the previous section.

The structure of Rust projects

The important parts of a Rust project are seen when we use Cargo to initialize a new project. Our hello the project has the following files:

.
├── Cargo.toml
├──.gitignore
└── src
   └── main.rs

Let’s understand what they stand for.

  • Cargo.toml: stores metadata for the package. The contents are grouped into two sections package and dependencies. The dependencies section contains records of the names and versions of external crates used in the project.

  • .gitignore: stores files that Git should not track. Git is Rust's official version control software, and the cargo new command initializes Git in the directory. When Rust code is built or compiled, a target/ sub-directory is generated containing the build files. Adding it to the .gitignore file is good practice to avoid pushing large unnecessary files to GitHub.

  • src/main.rs: The src/ subdirectory is where all Rust code is normally written. The main.rs file is the entry point for all binary crates.

Note: With Cargo, you can create two types of projects (preferably called packages or crates): binary packages/crates and library packages/crates. Let’s see how these are done. Run:

cargo new libpkg --lib

You should see a new libpkg project containing a lib.rs file inside the src/ directory instead of a main.rs file. You’ll also notice an auto-generated test code inside the lib.rs file. Library crates do not have/need a main function and do not allow the cargo run command. You can only use the tests to check your code by running cargo test. You can trick Cargo by creating a main.rs file inside the src/ directory, but if you publish the project as a package, remember to delete it.

Understanding basic syntax and structure

We have been introduced to the main function from the “Hello World” code above. Most programming languages like Go and C/C++ also have the main function as the entry-point function. But the difference is that Rust’s main function can behave like every other function by accepting and returning values. Weird, right?

Let’s go further with understanding Rust’s syntax for now.

Comments for documentation are made with the double forward slash: //. Try this out in the main.rs file:

fn main() {
	// greet me on the terminal
	println!("Hello World!");
}

The fn keyword is the Rust way of specifying that a code block is a function. All expressions and separate statements in Rust code must end with a semicolon, just like in C/C++. If you remove or skip it, the compiler panics.

Remove the semicolon after the closing brackets to print out the greeting and running cargo run.

Surprise! It still works. Why? The Rust compiler can infer the meaning of your code even if you omit the semicolon at the end of a statement. Smart, right?

Let’s update the main.rs file to this:

fn main() {
    let x = 5 // there is a missing semicolon here.
    println!("The value of x is: {}", x) // there is also a missing semicolon here that can be omitted.
}

The output should be:

❯ cargo run
   Compiling hello v0.1.0 (/.../workspace/hello)
error: expected `;`, found `println`
 --> src/main.rs:2:14
  |
2 |     let x = 5
  |              ^ help: add `;` here
3 |     println!("The value of x is: {}", x)
  |     ------- unexpected token

error: could not compile `hello` (bin "hello") due to previous error

The compiler tells you exactly what went wrong: expected ; . It further gives you a helping hand: help: add ; here.

Add the semicolon at the end of line 2, and run the program. This works. But it isn’t ideal. Add another semicolon at the end of line 3 too. There you go; a properly written Rust code.

The let keyword is a statement and the println! keyword (called a macro in Rust) is an expression. We will learn more about the use of the let keyword in the next section.

If you come from a C/C++ background, you can confirm that the main function in Rust receives arguments and return statements by updating the main.rs file and running it:

fn main() -> () {
    let x = 5;
    println!("The value of x is: {}", x);
} 

The -> symbol is how you specify return values. This is a more robust way to write the main function in Rust.

Whenever you're ready

There are 4 ways we can help you become a great backend engineer:

The MB Platform

Join 1000+ backend engineers learning backend engineering. Build real-world backend projects, learn from expert-vetted courses and roadmaps, track your learnings and set schedules, and solve backend engineering tasks, exercises, and challenges.

The MB Academy

The “MB Academy” is a 6-month intensive Advanced Backend Engineering BootCamp to produce great backend engineers.

Join Backend Weekly

If you like post like this, you will absolutely enjoy our exclusive weekly newsletter, Sharing exclusive backend engineering resources to help you become a great Backend Engineer.

Get Backend Jobs

Find over 2,000+ Tailored International Remote Backend Jobs or Reach 50,000+ backend engineers on the #1 Backend Engineering Job Board