Rust #1: Creating your development environment

cthutu

Matt Davies

Posted on June 19, 2021

Rust #1: Creating your development environment

Overview

This blog is my attempt at documenting what I've learnt on my journey using Rust. I have worked as a video game programmer for over 25 years and as a result have used C++ as my main language of choice, even for personal projects. Today, I will never touch C++ willingly again since discovering and learning about Rust. I will restrict C++'s use to my day job, and that's all. In my opinion, Rust has made C++ redundant for writing new software.

I have managed to hone my development environment for Rust programming over the last few months and I want to share my approach. This is what my first article is about - setting up a development environment for effective Rust programming.

Prerequisites

I will assume the reader knows their way around a terminal (or shell). That is, they know how to make folders, set the current folder, and launch commands all via their terminal.

I will also assume that the reader has prior programming experience, perhaps in C, C++ or similar language. These guides will not be teaching you how to program, but rather how to apply your current programming skills to a new language.

You will also need an internet connection. The Rust build system will constantly download packages (called crates) from the world wide web.

Installing Rust

The best way to install Rust is using the tool Rustup. Windows' users will require downloading an executable, but for operating systems with more sane command shells, a single cut & paste will suffice.

Hop over to http://rustup.rs and follow the instructions. After running the executable rustup-init.exe on Windows, or executing the shell command on other operating systems, you will see a screen-full of text and 3 options:

image

Go ahead and choose option 1 and you will have Rust and its associated tools installed. To make sure, open up your favourite terminal and type:

$ rustc --version
Enter fullscreen mode Exit fullscreen mode

if everything went well (and I hope it does because this guide will not help you otherwise), you should see something like:

rustc 1.53.0 (53cb7b09b 2021-06-17)
Enter fullscreen mode Exit fullscreen mode

The installation procedure involves rustup downloading and installing "components". The components you might have seen being installed would be:

  • cargo - the ultimate, bad-ass package/build/do-everything tool that will be the centre of your Rust-programming experience.
  • clippy - this is a linting tool that can scan your code and make suggestions on how to improve it, or catch problems. This is an essential part of your workflow.
  • rust-docs - a cargo plug-in that provides auto-generation of documentation from your source code. If you're familiar with Doxygen, it's similar to that.
  • rustc - the compiler, nuff said!
  • rustfmt - Rust's very opinionated code formatter. It will auto-format your code according to a set of standards so that everyone's Rust code looks the same. This can be annoying at first but you reap the benefits if you stick with it.

Your first Rust program

Let's try the obligatory Hello World program. And because of Cargo, you can do this without typing a line of code! Most guides, will demonstrate how to use rustc but you do not need to touch this directly. You will use Cargo to check, build, test, benchmark and run your code as well as many other things. So jump into your favourite terminal, cd to your folder you've set aside for Rust development (I use Windows and so my folder of choice will be r:\) and type:

$ cargo new hello
$ cd hello
Enter fullscreen mode Exit fullscreen mode

You will get a message back telling you that you've created a binary package called 'hello'.

Let's look inside your folder that you've just created. First, you will see a Cargo.toml file. This gives information to Cargo on how to build your project and other meta-data (like authorship, version etc). We'll cover this later.

Secondly, there is a hidden file and directory related to git, which is the default source control system used by most Rust programmers. You can switch it to Mercurial or something else. To read about that, rush over to https://doc.rust-lang.org/cargo/commands/cargo-new.html and checkout the --vcs command line option. I will be ignoring this file and folder in this guide.

Finally, there is a src folder, and that is where all your rust source code lies. If you look inside, you will see a lonely file called main.rs. Let's view it. You can use type (Windows) or cat (other operating systems) to show the contents of src/main.rs:

fn main() {
    println!("Hello, world!");
}
Enter fullscreen mode Exit fullscreen mode

The hello world program has already been written, courtesy of Cargo. Let's examine the code:

  • fn marks the start of a function and in this case defines the function called main. All Rust applications start at this function, like C/C++ does. If you build with Cargo, which we will, there also has to be a main.rs file in the root of the src folder. The parentheses following it usually contain parameters (we have none right now), and the code belonging to that function is in between the braces. If you are used to C, C++, C#, JavaScript etc, all this will be familiar to you.
  • println! will print text to standard output. The exclamation mark in the name indicates that println! is not a function but a macro. As in Lisp macros, not the useless C style macros. Rust can generate code during compilation. In this case, println! will expand to many statements, depending on its arguments, that will be inserted at the point of the call.

OK, let's run it. Type:

$ cargo run
   Compiling hello v0.1.0 (R:\hello)
    Finished dev [unoptimized + debuginfo] target(s) in 1.11s
     Running `target\debug\hello.exe`
Hello, world!
Enter fullscreen mode Exit fullscreen mode

This will compile the code and run it. The astute of you will have noticed a new folder appearing called target. Dig deeper and inside that you will see a debug folder. Finally, inside that you will see the hello.exe you just built and that Cargo just ran.

In fact, the compiler output before the 'Hello, world!' message describes the type of application you just built (unoptimised and including debug information) and where to find it.

We can build and run an optimised one too:

$ cargo run --release
   Compiling hello v0.1.0 (R:\hello)
    Finished release [optimized] target(s) in 0.66s
     Running `target\release\hello.exe`
Hello, world!
Enter fullscreen mode Exit fullscreen mode

Here are some other Cargo commands worth knowing to begin with:

  • cargo build - this will just build the final executable. cargo run will call this implicitly if it needs to.
  • cargo clean - this will delete the target folder and all the generated files within.
  • cargo check - useful while writing code. This will compile your code but without generating the code. This is a lot faster than cargo build and is useful if you just want to check for syntax errors. Because of the excellent IDE integration that I will demonstrate later, this is rarely needed.

One more point, after building your executables, a Cargo.lock file appeared too. This file is generated to lock down versions of other packages you might have used to build your program. This allows the Rust compiler to recreate the exact executable after a clean. It ensures that the same versions of packages are downloaded, built and linked with your application as before. For now, we don't use packages so this file contains very little information. If you use source control (like Git), you should include this file during submission, along with Cargo.toml and your source code.

The Editor

The second step in starting to learn Rust is setting up your programming environment just right so as to minimise friction between you and your work. The main part of that decision is which editor to use. I've tried writing Rust code in Emacs, Vim, Sublime but the text editor I eventually settled on is Visual Studio Code. For me, it's the best product Microsoft have ever created.

Why Visual Studio Code? In my opinion, it has the most mature tooling for writing Rust code, and for interacting with Rust's ecosystem and build tools (i.e. Cargo).

So go over to https://code.visualstudio.com/ and download and install it.

Restart your terminal, and enter:

$ code .
Enter fullscreen mode Exit fullscreen mode

And lo and behold:

image

The terminal is nice and all but there's all that typing. Don't we do enough while coding? So let's see if we can build from Visual Studio Code (hereafter referred to as VS Code). Currently, it can't because it needs extensions.

VS Code Extensions

To turn VS Code into a powerhouse editor of Rust, we need to install some extensions. By clicking on the 5th icon on the left (it looks like 4 squares, with one of them offset) you open up the extensions interface. Use the search box to find the necessary extensions listed below, then click on the blue 'install' buttons beside the extension's entry:

  • rust-analyzer - provides the 'intellisense' for Rust and many other Rust-related utilities.
  • C/C++ - what??? You need a C/C++ extension? Yes, it contains the debugger and it's useful for inserting breakpoints and stepping through your code. You can also use CodeLLDB but for me, the C/C++ one feels quicker.

Also, there are some useful extensions to install, that while unnecessary, are very useful all the same:

  • crates - checks the versions of any crates you reference (more on that later).
  • Markdown All in One - you will be writing several markdown files (files with .md extensions). This extension makes it easier to do so and provides a useful preview mode.
  • Even Better TOML - some editor support for editing TOML files such as Cargo.TOML.
  • Rewrap - not related to Rust but provides the ability to reformat text (in markdown files and Rust comments) so that they fit on the lines better. By pressing Alt+Q, you can change a couple of long comments into several shorter ones that fit on the screen. As you will eventually see, Rust comments will take a big part in document generation.

If you clear the search box, you will see the extensions you've installed. Some buttons beside them may be labelled 'Reload Required', and if you do, press it to restart VS Code in a matter of seconds.

After installing rust-analyzer, you will see a dialog box pop up in the bottom right corner of the window. If you miss it, click on the notifications icon in the bottom-right of the window. It should look like this:

image

Click the 'Download now' button. If you don't see it, restart VS Code and it should show again. This will download the Rust Language Server (RLS), which is the external program that can analyse your source code and communicate back to VS Code, via the extension, to display useful information about your program, including auto-complete.

When downloaded, 'rust-analyzer' will get to work looking at your code. Open up src/main.rs using VS Code and you will see two new links above the main function:

image

Click on Run and VS Code will build and run your program. You can see the results in the terminal window that automatically opens up.

Alternatively, you can click on Debug to do the same thing. But right now VS Code isn't properly set up for debugging.

Debugging in VS Code

If you haven't used VS Code before, it probably isn't set up for debugging facilities like breakpoints. So press Ctrl+Comma to open up VS Code's settings window. Type breakpoints in the search bar at the top of this window, and make sure Debug: Allow Breakpoints Everywhere is selected:

image

While you're in the settings window, retype the search parameter to be formatter. Ensure Editor: Default Formatter is set to rust-analyzer and Editor: Format On Save is checked. This will allow the rustfmt component mentioned earlier to do its magic every time you save a file. It's a really nice time-saver. You can type code as messily as you want in the sweet knowledge that when you save, everything will move to its correct place, and missing trailing commas and spacing will be inserted.

Now you've done this, you can hit F9 while editing to add a breakpoint to the very line you're on. Try doing this now on the println! statement in src/main.rs:

image

Click on 'Debug' and see the code stop on the line before it executes it. Tap F10 to step over the next line and you will see 'Hello, world!' appear as it executes it. Press F5 to continue running, or Shift+F5 to stop debugging altogether. Don't forget to tap F9 again on the line with the breakpoint to remove it!

Launchers and Tasks in VS Code

These links in your code are very convenient, but it requires that you have src/main.rs open at the main function to see them. Wouldn't it be better if we could press a key and have it run or build?

First step is coercing VS Code to build your source code at a press of a key. Press F1 to bring up the command palette, which is an interface in VS Code that contains all the possible commands you can give to it. Now type build and the command menu will whittle down to a few entries, one of which is Tasks: Configure Default Build Task.

image

Select it. A new menu will appear with a list of possible Cargo commands. We want to choose rust: cargo build. This will create a new file in your project at .vscode/tasks.json. This file defines a task that VS Code can run. It calls cargo build and that's all. But let's improve on it.

Firstly, change the label entry to something shorter. It can be anything you want, but I usually change it to build. It will be referenced by another file later.

Secondly, we would like the terminal window in VS Code to clear every time we start a build so we can clear distinguish current messages from a previous build's messages. Add a comma at the end of the label line and then start typing presentation. After a few characters you should see an intellisense window open up suggesting presentation. Hit ENTER and VS Code will auto-complete the entry. Very handy! Now change the clear entry so it says true instead of false. The final version should look like this:

{
  "version": "2.0.0",
  "tasks": [
    {
      "type": "cargo",
      "command": "build",
      "problemMatcher": [
        "$rustc"
      ],
      "group": {
        "kind": "build",
        "isDefault": true
      },
      "label": "build",
      "presentation": {
        "echo": true,
        "reveal": "always",
        "focus": false,
        "panel": "shared",
        "showReuseMessage": true,
        "clear": true
      }
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Now, select File > Preferences > Keyboard Shortcuts from the menu and type build in the search bar of the new keyboard shortcuts window.

Double-click under the Keybinding column on the row that says Tasks: Run Build Task. This will allow you to bind a key to that command. Type F7 followed by ENTER and you're done. VS Code will warn you that other bindings use the same key, but ignore those as they will not conflict. I chose F7 because that's the usual key on the Visual Studio IDE for building and I am used to it. Of course, you can choose any other key combination that suits you.

Let's test it! Hit F7 now (or whichever key combination you chose) and the terminal window in VS Code will clear and start your build!

Now have it building at a press of a key, we want it to run (after building) at a press of another key. VS Code already has F5 set up for that task, but currently doesn't know how.

Click on the 4th icon on the left side of VS Code, the one that looks like a triangle with a bug on the corner. When you do this, you will see a new interface with a big Run and Debug button (but don't press it!).

image

Under that button, you should see a link that says create a launch.json file. To run your program at a press of a button, we need to create a launcher. Launchers are configured via this launch.json file, so go ahead and click on that link. You will see a dialog asking you to select and environment.

image

Choose C++ (Windows) if you're on Windows and you used the C/C++ extension, or choose C++ (GDB/LLDB) otherwise. This will create a new file called .vscode/launch.json. As before, let's make a few changes:

  1. Change the program value so it reads ${workspaceFolder}/target/debug/hello.exe. This needs to match the final executable you're running.
  2. Change the console value so it reads integratedTerminal and add a comma at the end.
  3. After the console entry, and the reason you added a comma, add the entry "preLaunchTask": "build". This is the line that references the label text you entered in tasks.json. They must match. This line tells the launcher to ensure that the build task is run first. We want to run the latest version of our program, don't we?

The final result will be:

{
  // Use IntelliSense to learn about possible attributes.
  // Hover to view descriptions of existing attributes.
  // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "(Windows) Launch",
      "type": "cppvsdbg",
      "request": "launch",
      "program": "${workspaceFolder}/target/debug/hello.exe",
      "args": [],
      "stopAtEntry": false,
      "cwd": "${fileDirname}",
      "environment": [],
      "preLaunchTask": "build"
    }
  ]
}
Enter fullscreen mode Exit fullscreen mode

Press F5 and the launcher should run. You should see in your terminal, build messages and the output of the program.

Occasionally, I have seen VS Code struggle with this. If it doesn't run, check your tasks.json and launch.json files, and run cargo clean from the terminal within your project folder.

Compiler Errors

This blog is getting a bit long now, so one last thing to talk about. What happens if you have errors in your code. The good news is that the rust-analyzer extension and VS Code will conspire to help you out. Let's add an error in our code. After the println! statement, add a new one:

fn main() {
    println!("Hello, world!");
    foo();
}
Enter fullscreen mode Exit fullscreen mode

Then hit save (Ctrl+S). rust-analyzer will generally only see your changes after saving. Now the error is in the program, without building multiple things will happen:

  1. A red line will be visible under foo. Move your mouse pointer over it and you will see a popup describing the error.
  2. Looking at the tab above your editing window, you will see that the filename has turned red with a number after it. (There is also a letter U but that's related to source control so ignore it). That number is how many errors there are. The tab text can turn yellow too, which means there are warnings but no errors. This time the number counts how many warnings there are. At a glance you can see how many errors in your program.
  3. On the scrollbar in your window, you will see a little red marker. This shows roughly where your error is.
  4. If your terminal window is still open in VS Code, you will see a number next to the Problems tab. Inside this tab, you will see a list of errors. Click on them to jump to the line of code with the error.

Wow, that's a lot of clues something has gone wrong with your program. Now hit your build key to run the build task (remember mine was F7). In the Terminal tab below you will see the output of the compiler. It usually has a better error message than seen elsewhere:

image

Rust provides the best error messages EVER! You can even go to your terminal and type rustc --explain E0765, as described, to explain the error above in more detail. Truly amazing!

Summary

That was a lot to go over. But we installed Rust and it's tools, installed VS Code and extended it to support Rust, and then showed how to use VS Code to build and run your program.

Part #2 will talk about the borrow checker, ownership, lifetimes and all those nice things. See you then!

💖 💪 🙅 🚩
cthutu
Matt Davies

Posted on June 19, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related