1: Why choose Rust?

This lesson has some overlap with the Rust - Language of the Future webinar: https://www.youtube.com/watch?v=4djxKiFrO0A that was held before the start of this course.

Hi, and welcome to the first lesson of the Rust Developer course. Since this is the beginning of the course, we should start at the beginning as well.

Introduction

Rust is a systems programming language that focuses on performance, reliability, and productivity. It has been designed to address the issues of memory safety and concurrent programming by not compromising on performance.

Despite only gaining traction in recent years, Rust is not that new, both in implementation and conceptually. It was originally invented by Graydon Hoare, a Mozilla developer at the time, in 2006, with the mission of creating a language for writing robust code. It is named after a species of fungi (the rust fungus) and because rust is a substring of robust.

Mozilla’s Involvement

In 2009, Rust caught the attention of Mozilla, which began sponsoring the project. Mozilla saw potential in Rust as a safer alternative to C++ for developing high-performance, oncurrent systems, especially in the context of web browsers. Rust was used to develop components for the Servo layout engine, an experimental browser engine aimed at improving the parallel processing of web content.

This was a difficult endeavor and required a high-level of trust from the side of Mozilla because the language was evolving rapidly at the time and breaking changes would be introduced on essentially weekly basis.

Stable Release

Rust 1.0, the first stable release, was announced in May 2015. This release marked a significant milestone, providing developers with a stable platform for building applications. Since then, Rust has followed a regular release schedule, with new stable versions being released every six weeks.

All changes pass from nightly, through beta into the stable channel. In this course, we will be using the stable channel, but even the nightly one tends to be reliable enough that some companies (usually startups) are not afraid to put it into production.

Philosophy of Rust

Over time, the philosophy of Rust crystalized into a focus on the following areas, with these solutions to them:

  • Performance: Rust offers performance similar to C and C++ but with additional safety features.
  • Memory Safety Guarantees: Rust’s borrow checker ensures memory safety and prevents common bugs like null pointer dereferencing and data races.
  • Concurrency: Rust makes it easier to reason about concurrency, enabling developers to write multi-threaded applications with confidence.

Building up on the last point, Rust tries to make it easy to reason about everything by being very explicit and not doing any "behind the scenes magic". This makes Rust code explicit and helps you think about what you are doing, but also slower to write than many other languages.

Key features of Rust

Rust is a language that has a rather unusual pedigree. The oldest and most historically prominent for Rust was OCaml and SML, a functional programming language (although not a pure one like Haskell). Nowadays, OCaml's influences are mostly seen on Rust's syntax and keywords.

There is a number of other influences, which can be found here:

https://doc.rust-lang.org/reference/influences.html

The result is a language that claims to not be overly original, but rather one that brings features from more obscure languages and discoveries from academic circles into a form that is suitable for mainstream development.

We can briefly summarize the key features of Rust like this:

1. Syntax:

  • OCaml- and C-Like Syntax: Rust's syntax is influenced not just by OCaml, but also by C, making it familiar to developers with a background in languages with similar heritage.
  • Pattern Matching: Rust supports powerful pattern matching, which is a versatile tool for handling structured data and control flow.
  • Macros: Rust supports hygienic macros for metaprogramming, allowing code generation and reducing boilerplate.

2. Semantics:

  • Ownership System: Rust introduces a unique ownership system with borrowing and lifetime concepts, enforcing memory safety and preventing data races at compile-time.
  • Expression-Based: Rust is predominantly expression-based, meaning most constructs return a value, contributing to concise and expressive code.
  • Immutable by Default: Variables are immutable by default, which encourages functional programming principles and helps prevent unintended side effects.

3. Typing:

  • Static Typing: Rust is statically typed, which means types are checked at compile-time, reducing runtime errors.
  • Type Inference: While being statically typed, Rust also offers type inference, allowing for more concise code without explicit type annotations.
  • Generics and Traits: Rust supports generics and traits, providing powerful tools for polymorphism and code reuse.
  • Algebraic Data Types: Rust supports algebraic data types (enums and structs), facilitating expressive modeling of data structures.
  • Lifetime Annotations: Lifetimes are part of Rust’s type system, enabling fine-grained control over object lifetimes and ensuring memory safety.

4. Concurrency:

  • Fearless Concurrency: Rust features ownership, borrowing, and lifetimes, which together enable "fearless concurrency", preventing data races and enabling safe parallel execution.
  • Async/Await: Rust supports asynchronous programming with async/await syntax, making it easier to write non-blocking code.

5. Memory Management:

  • No Garbage Collector: Rust does not use a garbage collector, relying instead on its ownership system to manage memory, ensuring predictable performance.
  • Zero-Cost Abstractions: Rust’s abstractions have minimal or no runtime overhead, ensuring high performance.

6. Error Handling:

  • Result and Option Types: Rust uses the Result and Option types for error handling, making errors explicit and encouraging developers to handle them appropriately.
  • Panic: Rust includes a panic mechanism for dealing with unrecoverable errors, with options for customization.

In the following lessons, we will examine all of these points, but first, let's start by running a hello world.

Hello World

To get started, here is the quintessential "Hello, World!" program written in Rust:

fn main() {
    println!("Hello, World!");
}

You can use the Rust Playground website to try out it and modify it, or you can use the play button in the top right corner of the code example.

Let's break down this simple program:

  1. fn main()

    • This line defines the main function, which is the entry point of every Rust program. The fn keyword is used to declare a new function, followed by the name of the function, main, and a pair of parentheses () indicating that this function takes no arguments. Keywords in Rust are typically short. In Rust, the main function does not have to return anything. The program will exit with code 0 unless otherwise specified.
  2. { and }

    • These curly braces { } denote the start and end of the function body. All Rust functions are enclosed in curly braces. This will be familiar to you, if you used any of the languages influenced by C.
  3. println!("Hello, World!");

    • This line is the body of the main function. The println! macro is called to print the string "Hello, World!" to the console followed by a new line. The ! after println indicates that this is a macro call rather than a regular function call. Macros in Rust are a form of metaprogramming and are used for a variety of tasks, including printing to the console. The string "Hello, World!" is passed as an argument to the println! macro. We will discuss why it is a macro in a future lesson.
    • The line ends with a semicolon ;, which is used to terminate expressions in Rust.

When you run this program, the Rust compiler will compile the source code into an executable binary. Upon execution, the program will print Hello, World! to the console. This program demonstrates the basic structure of a Rust program, including function definition, macro invocation, and expression termination.

Advanced Hello World

Here is a slightly more advanced example, that greets someone in particular:

use std::io;

fn main() {
    // Print a message to ask for the user's name
    println!("Enter your name:");

    // Create a new String to store the user input
    let mut name = String::new();

    // Read the user input and store it in the 'name' variable
    io::stdin().read_line(&mut name).expect("Failed to read line");

    // Trim the trailing newline character from the input
    let name = name.trim();

    // Print a greeting message with the user's name
    println!("Hello, {}!", name);
}

This program first prints "Enter your name:" to the console and waits for user input. It then reads the input into the name variable. Before printing out the greeting message, it trims the trailing newline character from the input. Finally, it prints "Hello, !" to the console.

Notice the .expect(), which constitutes error handling (gracefully shutdown program with this message if .read_line() returns an error), the .trim() (the read line contains the trailing newline character) and the re-declaration of the name variable as immutable - Rust allows variable shadowing.

Development environment

Running examples through the Rust playground will not be enough for us, we need to set up a development environment.

The guide for installing Rust focuses mostly on Linux and Visual Studio Code as our editor of choice. The reason why we picked VS Code is because some Rust tools are developed in tandem with their respective VS Code extensions, ensuring the greatest level of compatibility and maturity.

You are welcome to choose any other editor, or IDE. Here is a handy website to help you on your way:

https://areweideyet.com/

Without further ado, let's get into it.

Step 1: Install Rust

  1. Open a Terminal or Command Prompt

    • On Windows, you can open the Command Prompt by searching for cmd in the Start menu. Rustup also provides a graphical installer that you can use if you do not want to use the Command Prompt.
    • On macOS, you can open the Terminal from the Applications folder under Utilities.
    • On Linux, you can open a terminal window (how depends on your distribution, but you probably know already)
  2. Download and Install Rustup

    • Run the following command in the Terminal or Command Prompt:
      curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
      
    • Follow the on-screen instructions to complete the installation.
  3. Configure the Path

    • Once installed, close the current Terminal or Command Prompt and open a new one.
    • Run the following command to add Rust to your system’s PATH variable:
      source $HOME/.cargo/env
      
    • On Windows, Rustup should automatically configure the PATH variable for you.
  4. Verify Installation

    • Run the following command to verify that Rust is installed correctly:
      rustc --version
      
      At the time of this writing, version 1.72 is the latest.

Step 2: Install Visual Studio Code (VSCode)

  1. Download VSCode

  2. Install VSCode

    • Follow the installation instructions for your operating system:
      • Windows: Run the installer and follow the prompts.
      • macOS: Open the downloaded .zip file and drag Visual Studio Code to your Applications folder.
      • Linux: Follow the platform-specific instructions provided on the download page.
  3. Launch VSCode

    • Once installed, launch Visual Studio Code from your applications menu or by searching for it. On Linux, you can also launch VS Code from the terminal by writing code.

Step 3: Install the rust-analyzer Extension

  1. Open VSCode

    • If not already open, launch Visual Studio Code.
  2. Access Extensions View

    • Click on the Extensions view icon on the Sidebar (or press Ctrl+Shift+X / Cmd+Shift+X).
  3. Search and Install rust-analyzer

    • In the Extensions view search box, type rust-analyzer.
    • Locate rust-analyzer in the search results and click on the Install button.
    • NOTE: Do not install any other Rust extension, rust-analyzer is the current and best one, and the older rust extension has conflicts with it.
  4. Reload Window

    • After the installation is complete, you may be prompted to reload the window to activate the extension. If so, click on the Reload button. The VS Code extension will also automatically fetch rust-analyzer toolchain component, if your Rust installation did not include it already.
  5. Configure rust-analyzer (Optional)

    • You can configure rust-analyzer settings by accessing the settings menu (Ctrl+, / Cmd+,) and searching for rust-analyzer. For the purposes of our course, no configuration should be necessary on most systems.

Now, you have Rust, Visual Studio Code, and the rust-analyzer extension installed and configured on your system, ready for Rust development.

Running Hello World in VS Code

To verify everything is working correctly, we will run the previous "Hello world!" example with our new setup.

Step 1: Create a New Cargo Project

  1. Open a Terminal or Command Prompt

    • Navigate to the directory where you want to create your new Rust project.
  2. Create a New Cargo Project

    • Run the following command, replacing hello_world with the name you'd like to give to your project:
      cargo new hello_world
      
    • This command uses the Cargo build system/package manager to create a new directory named hello_world with the initial project files.
  3. Navigate to the Project Directory

    • Change into the newly created project directory:
      cd hello_world
      

Step 2: Open the Project in VSCode

  1. Launch VSCode

    • If you do not have your VSCode opened yet, launch.
  2. Open the Project Folder

    • Click on the “File” menu in the top left corner and select “Open Folder…”.
    • Navigate to the hello_world directory you've just created and select “Open”.
  3. Explore the Project Structure

    • In the Explorer sidebar, you should see the hello_world project structure. The main file of interest is src/main.rs, which contains the main function of your application.

Step 3: Review and Run the Code

  1. Review the Generated Code

    • Open the src/main.rs file and review the generated code. It should contain the following "Hello, World!" program:
      fn main() {
          println!("Hello, World!");
      }
  2. Open the Terminal in VSCode

    • Click on the “Terminal” menu at the top, and select “New Terminal”. A terminal should open at the bottom of the VSCode window, with the working directory set to your project folder. Using a terminal built into VSCode is typically more ergonomic than working with a separate window.
  3. Run the Project

    • In the terminal, type the following command and press Enter to run the project:
      cargo run
      
    • Cargo will compile and run the project, and you should see the output Hello, World! in the terminal. If you see an error, you may be missing a dependency on your system - usually a C compiler.

Step 4: (Optional) Edit the Code

  • Feel free to edit the println! macro in src/main.rs to print a different message, save the file (Ctrl+S / Cmd+S), and rerun the project with cargo run to see your changes.

Congratulations, you’ve successfully created a new Rust Cargo project, opened it in Visual Studio Code, and ran the "Hello, World!" program! This concludes our setup for Rust.

Common Cargo commands

Cargo is Rust’s build system and package manager, and it comes with a variety of commands to help you develop Rust applications efficiently. In fact, it is one of the reasons why developers like Rust in the first place - gone is the dependency management hell common in C and other languages.

Here are some common Cargo commands that you will use frequently when working with Rust projects:

1. cargo new <project_name>

  • Description: Creates a new Rust project with the specified name. It generates a directory with the project name, initializes a Git repository, and creates initial files.
  • Example Usage: cargo new my_project

2. cargo build

  • Description: Compiles the current project. By default, it creates a debug build, which can be several orders of magnitudes slower and significantly larger. Use the --release flag for an optimized build.
  • Example Usage: cargo build or cargo build --release

3. cargo run

  • Description: Compiles and runs the current project.
  • Example Usage: cargo run or cargo run --release

4. cargo check

  • Description: Quickly checks the current project for errors without producing executable binaries. It is significantly faster than cargo build, and often will be enough when developing Rust.
  • Example Usage: cargo check

5. cargo test

  • Description: Compiles and runs tests for the current project. This will verify doctests too, we will discuss testing later.
  • Example Usage: cargo test

6. cargo doc

  • Description: Builds documentation for the current project.
  • Example Usage: cargo doc or cargo doc --open to also open it in your browser.

7. cargo update

  • Description: Updates dependencies as listed in Cargo.lock.
  • Example Usage: cargo update

8. cargo clean

  • Description: Removes the target/ directory where Cargo puts built artifacts. It is a good idea to run this command every once in a while, since the target/ folder can grow pretty large for applications that are being developed for a long time.
  • Example Usage: cargo clean

9. cargo fmt

  • Description: Formats the code in the current project according to the Rust style guidelines. Keep in mind that most editors will be able to already format code properly on their own, so you may not use this command very often.
  • Example Usage: cargo fmt

NOTE: Most of these commands (if it makes sense) will also automatically fetch and setup all of your application's dependencies, there is no explicit step like npm install. There exists a cargo install command, however, which is used to install Rust applications to your system - in other words, it is similar to npm install --global or yarn global add.

Familiarity with these commands will help you effectively navigate and manage your Rust projects. Feel free to come back to this section to refer to the commands written here.

Homework

For the first lesson, we will start simple.

Your assignment is to set up your development environment for Rust and try it out.

Description:

These instructions are essentially slightly condensed versions of the ones above, so refer to them in case you run into any issues. Keep in mind that you can also always message me on Discord, if you run into problems.

  1. Install Rust:

    • Follow the guide provided earlier to install Rust on your system. Ensure that it's properly installed by using the rustc --version command in your terminal or command prompt.
  2. Install Visual Studio Code (VSCode) - OPTIONAL:

    • Download and install Visual Studio Code from the official website. This will be your Integrated Development Environment (IDE) for writing and managing Rust code.
  3. Set up the rust-analyzer extension - OPTIONAL:

    • Once you have VSCode installed, add the rust-analyzer extension from the Extensions view in VSCode. This will provide you with enhanced functionality and support for Rust development within the IDE.

(The previous two steps are optional because you are welcome to use any editor you want - VS Code is only my recommendation)

  1. Create and Run a "Hello, World!" Cargo Project:

    • Utilize the knowledge gained from today's lesson to create a new Rust project using Cargo. Write the "Hello, World!" program and run it successfully within VSCode.
  2. Set Up a GitHub Repository:

    • If you don't have a GitHub account yet, please create one.
    • Once your account is set up, create a new repository to store your Rust project.
    • Follow GitHub's instructions to push your local Rust "Hello, World!" project to this new repository.
  3. Modify and Update:

    • Feel free to make any modifications to the "Hello, World!" example. This could be as simple as changing the message printed to the console or experimenting with additional Rust syntax and features.
    • You don't know much Rust yet, so the degree to which you want to play around with the program is up to you.
    • After making your modifications, commit the changes, and push them to your GitHub repository.

Submission:

  • Upon completion, please share the link to your GitHub repository containing the modified "Hello, World!" project on the class submission platform.
  • Ensure that your repository is public so that it can be accessed and reviewed.

Deadline:

  • Please complete and submit this assignment until Tuesday, October 9 at the latest. It will, however, make your life much easier if you manage to do it by the next lesson - Thursday, October 5

This exercise is our first foray into the world of Rust programming. By installing the necessary tools, writing a basic program, and pushing the project to github GitHub, you are laying down the foundational skills needed for completing the rest of the course. Don’t hesitate to reach out if you have any questions or encounter any issues; I am here to help!

Happy oxidation!