This article gives you a chance to experience Rust firsthand. It demonstrates how to use the compiler and then moves on to writing a quick program. We tackle full projects in later blogs.
Note To install Rust, use the official installers provided at https://rustup.rs/.
Cheating your way to “Hello, world!”
The first thing that most programmers do when they reach for a new programming language is to learn how to print “Hello, world!” to the console. You’ll do that too, but with flair. You’ll verify that everything is in working order before you encounter annoying syntax errors.
If you use Windows, open the Rust command prompt that is available in the Start menu after installing Rust. Then execute this command:
C:\> cd %TMP%
If you are running Linux or macOS, open a Terminal window. Once open, enter the following:
$ cd $TMP
From this point forward, the commands for all operating systems should be the same. If you installed Rust correctly, the following three commands will display “Hello, world!” on the screen (as well as a bunch of other output):
$ cargo new hello
$ cd hello
$ cargo run
Here is an example of what the entire session looks like when running cmd.exe on MS Windows:
C:\> cd %TMP%
C:\Users\Tim\AppData\Local\Temp\> cargo new hello
Created binary (application) `hello` project
C:\Users\Tim\AppData\Local\Temp\> cd hello
C:\Users\Tim\AppData\Local\Temp\hello\> cargo run
Compiling hello v0.1.0 (file:/ / /C:/Users/Tim/AppData/Local/Temp/hello)
Finished dev [unoptimized + debuginfo] target(s) in 0.32s
Running `target\debug\hello.exe`
Hello, world!
And on Linux or macOS, your console would look like this:
$ cd $TMP
$ cargo new hello
Created binary (application) `hello` package
$ cd hello
$ cargo run
Compiling hello v0.1.0 (/tmp/hello)
Finished dev [unoptimized + debuginfo] target(s) in 0.26s
Running `target/debug/hello`
Hello, world!
If you have made it this far, fantastic! You have run your first Rust code without needing to write any Rust. Let’s take a look at what just happened.
Rust’s cargo tool provides both a build system and a package manager. That means cargo knows how to convert your Rust code into executable binaries and also can manage the process of downloading and compiling the project’s dependencies.
cargo new
creates a project for you that follows a standard template. The tree command can reveal the default project structure and the files that are created after issuing cargo new
:
$ tree hello
hello
├── Cargo.toml
└── src
└── main.rs
1 directory, 2 files
All Rust projects created with cargo have the same structure. In the base directory, a file called Cargo.toml describes the project’s metadata, such as the project’s name, its version, and its dependencies. Source code appears in the src directory. Rust source code files use the .rs filename extension. To view the files that cargo new creates, use the tree command.
The next command that you executed was cargo run. This line is much simpler to grasp, but cargo actually did much more work than you realized. You asked cargo to run the project. As there was nothing to actually run when you invoked the command, it decided to compile the code in debug mode on your behalf to provide maximal error information. As it happens, the src/main.rs file always includes a “Hello, world!” stub. The result of that compilation was a file called hello (or hello.exe). The hello file was executed, and the result printed to your screen.
Executing cargo run
has also added new files to the project. We now have a Cargo.lock file in the base of our project and a target/ directory. Both that file and the directory are managed by cargo. Because these are artifacts of the compilation process, we won’t need to touch these. Cargo.lock is a file that specifies the exact version numbers of all the dependencies so that future builds are reliably built the same way until Cargo.toml is modified.
Running tree again reveals the new structure created by invoking cargo run to compile the hello project:
All Rust projects created with cargo have the same structure. In the base directory, a file called Cargo.toml describes the project’s metadata, such as the project’s name, its version, and its dependencies. Source code appears in the src directory. Rust source code files use the .rs filename extension. To view the files that cargo new
creates, use the tree
command.
The next command that you executed was cargo run
. This line is much simpler to grasp, but cargo actually did much more work than you realized. You asked cargo to run the project. As there was nothing to actually run when you invoked the command, it decided to compile the code in debug mode on your behalf to provide maximal error information. As it happens, the src/main.rs file always includes a “Hello, world!” stub. The result of that compilation was a file called hello (or hello.exe). The hello file was executed, and the result printed to your screen.
Executing cargo run
has also added new files to the project. We now have a Cargo.lock file in the base of our project and a target/ directory. Both that file and the directory are managed by cargo. Because these are artifacts of the compilation process, we won’t need to touch these. Cargo.lock is a file that specifies the exact version numbers of all the dependencies so that future builds are reliably built the same way until Cargo.toml is modified.
Running tree
again reveals the new structure created by invoking cargo run
to compile the hello project:
$ tree --dirsfirst hello
hello
├── src
│ └── main.rs
├── target
│ └── debug
│ ├── build
│ ├── deps
│ ├── examples
│ ├── native
│ └── hello
├── Cargo.lock
└── Cargo.toml
For getting things up and running, well done! Now that we’ve cheated our way to “Hello, World!”, let’s get there via the long way.
Your first Rust program
For our first program, we want to write something that outputs the following text in multiple languages:
Hello, world!
Grüß Gott!
ハロー・ワールド
You have probably seen the first line in your travels. The other two are there to highlight a few of Rust’s features: easy iteration and built-in support for Unicode. For this program, we’ll use cargo to create it as before. Here are the steps to follow:
- Open a console prompt.
- Run
cd %TMP%
on MS Windows; otherwisecd $TMP
. - Run
cargo new hello2
to create a new project. - Run
cd hello2
to move into the project’s root directory. - Open the file src/main.rs in a text editor.
- Replace the text in that file with the text in listing 1.1.
The code for the following listing is in the source code repository. Open ch1/ch1-hello2/src/hello2.rs.
Listing 1.1 “Hello World!” in three languages
fn greet_world() {
println!("Hello, world!"); // <1>
let southern_germany = "Grüß Gott!"; // <2>
let japan = "ハロー・ワールド"; // <3>
let regions = [southern_germany, japan]; // <4>
for region in regions.iter() { // <5>
println!("{}", ®ion); // <6>
}
}
fn main() {
greet_world(); // <7>
}
① The exclamation mark indicates the use of a macro, which we’ll discuss shortly.
② Assignment in Rust, more properly called variable binding, uses the let keyword.
③ Unicode support is provided out of the box.
④ Array literals use square brackets.
⑤ Many types can have an iter()
method to return an iterator.
⑥ The ampersand “borrows” region for read-only access.
⑦ Calls a function. Note that parentheses follow the function name.
Now that src/main.rs is updated, execute cargo run from the hello2/
directory. You should see three greetings appear after some output generated from cargo itself:
$ cargo run
Compiling hello2 v0.1.0 (/path/to/ch1/ch1-hello2)
Finished dev [unoptimized + debuginfo] target(s) in 0.95s
Running `target/debug/hello2`
Hello, world!
Grüß Gott!
ハロー・ワールド
Let’s take a few moments to touch on some of the interesting elements of Rust from listing 1.1.
One of the first things that you are likely to notice is that strings in Rust are able to include a wide range of characters. Strings are guaranteed to be encoded as UTF-8. This means that you can use non-English languages with relative ease.
The one character that might look out of place is the exclamation mark after println
. If you have programmed in Ruby, you may be used to thinking that it is used to signal a destructive operation. In Rust, it signals the use of a macro. Macros can be thought of as fancy functions for now. These offer the ability to avoid boilerplate code. In the case of println
!, there is a lot of type detection going on under the hood so that arbitrary data types can be printed to the screen.