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:
-
fn main()- This line defines the main function, which is the entry point of every Rust program.
The
fnkeyword 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 code0unless otherwise specified.
- This line defines the main function, which is the entry point of every Rust program.
The
-
{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.
- These curly braces
-
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!afterprintlnindicates 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 theprintln!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.
- This line is the body of the main function. The
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,
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:
Without further ado, let's get into it.
Step 1: Install Rust
-
Open a Terminal or Command Prompt
- On Windows, you can open the Command Prompt by searching for
cmdin 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)
- On Windows, you can open the Command Prompt by searching for
-
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.
- Run the following command in the Terminal or Command Prompt:
-
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.
-
Verify Installation
- Run the following command to verify that Rust is installed correctly:
At the time of this writing, version 1.72 is the latest.rustc --version
- Run the following command to verify that Rust is installed correctly:
Step 2: Install Visual Studio Code (VSCode)
-
Download VSCode
- Visit the Visual Studio Code download page and download the version appropriate for your operating system.
-
Install VSCode
- Follow the installation instructions for your operating system:
- Windows: Run the installer and follow the prompts.
- macOS: Open the downloaded
.zipfile and drag Visual Studio Code to your Applications folder. - Linux: Follow the platform-specific instructions provided on the download page.
- Follow the installation instructions for your operating system:
-
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.
- 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
Step 3: Install the rust-analyzer Extension
-
Open VSCode
- If not already open, launch Visual Studio Code.
-
Access Extensions View
- Click on the Extensions view icon on the Sidebar (or press
Ctrl+Shift+X/Cmd+Shift+X).
- Click on the Extensions view icon on the Sidebar (or press
-
Search and Install
rust-analyzer- In the Extensions view search box, type
rust-analyzer. - Locate
rust-analyzerin the search results and click on the Install button. - NOTE: Do not install any other Rust extension,
rust-analyzeris the current and best one, and the olderrustextension has conflicts with it.
- In the Extensions view search box, type
-
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-analyzertoolchain component, if your Rust installation did not include it already.
- 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
-
Configure
rust-analyzer(Optional)- You can configure
rust-analyzersettings by accessing the settings menu (Ctrl+,/Cmd+,) and searching forrust-analyzer. For the purposes of our course, no configuration should be necessary on most systems.
- You can configure
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
-
Open a Terminal or Command Prompt
- Navigate to the directory where you want to create your new Rust project.
-
Create a New Cargo Project
- Run the following command, replacing
hello_worldwith 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_worldwith the initial project files.
- Run the following command, replacing
-
Navigate to the Project Directory
- Change into the newly created project directory:
cd hello_world
- Change into the newly created project directory:
Step 2: Open the Project in VSCode
-
Launch VSCode
- If you do not have your VSCode opened yet, launch.
-
Open the Project Folder
- Click on the “File” menu in the top left corner and select “Open Folder…”.
- Navigate to the
hello_worlddirectory you've just created and select “Open”.
-
Explore the Project Structure
- In the Explorer sidebar, you should see the
hello_worldproject structure. The main file of interest issrc/main.rs, which contains the main function of your application.
- In the Explorer sidebar, you should see the
Step 3: Review and Run the Code
-
Review the Generated Code
- Open the
src/main.rsfile and review the generated code. It should contain the following "Hello, World!" program:fn main() { println!("Hello, World!"); }
- Open the
-
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.
-
Run the Project
- In the terminal, type the following command and press
Enterto 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.
- In the terminal, type the following command and press
Step 4: (Optional) Edit the Code
- Feel free to edit the
println!macro insrc/main.rsto print a different message, save the file (Ctrl+S/Cmd+S), and rerun the project withcargo runto 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
--releaseflag for an optimized build. - Example Usage:
cargo buildorcargo build --release
3. cargo run
- Description: Compiles and runs the current project.
- Example Usage:
cargo runorcargo 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 docorcargo doc --opento 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 thetarget/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.
-
Install Rust:
- Follow the guide provided earlier to install Rust on your system. Ensure that it's properly installed by
using the
rustc --versioncommand in your terminal or command prompt.
- Follow the guide provided earlier to install Rust on your system. Ensure that it's properly installed by
using the
-
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.
-
Set up the
rust-analyzerextension - OPTIONAL:- Once you have VSCode installed, add the
rust-analyzerextension from the Extensions view in VSCode. This will provide you with enhanced functionality and support for Rust development within the IDE.
- Once you have VSCode installed, add the
(The previous two steps are optional because you are welcome to use any editor you want - VS Code is only my recommendation)
-
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.
-
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.
-
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!