
Welcome to Learn to Code with Rust. Rust is a systems programming language optimized for speed and safety. In this lesson, we'll introduce the language's features, its history, and some fun tidbits about its community.
Source code is the code that we developers write. The Rust compiler is a program that translates our source code into an executable program called a binary or a binary executable. This lesson also offers an introduction to the compiler and general programming advice to reduce your chance of errors.
Access the Terminal application, a command-line interface for interacting with the operating system. Learn some basic commands for file system navigation, printing contents, and more.
XCode Command Line Tools is a dependency (requirement) of installing the Rust compiler. In this lesson, we set up the Command Line Tools through the Terminal.
In this lesson, we'll install Rust on macOS operating systems. The installation also includes the rustup tool for managing Rust and the cargo tool for managing projects.
Visual Studio Code (VSCode) is a free text editor for writing source code in various programming languages. In this lesson, we install the text editor and several extensions for Rust development.
In this lesson, we run a command from VSCode's Command Line Palette to ensure the code command works from the Terminal. We can use code . to open up the current directory.
Access the PowerShell application, a command-line interface for interacting with the operating system. Learn some basic commands for file system navigation, printing contents, and more.
Windows has 32-bit and 64-bit versions; you'll need to know your operating system version to setup Rust. This video walks you through finding out the version; memorize the value.
In order to setup Rust, we'll need to install Visual Studio, a text editor from Microsoft which includes a dependency called the Visual Studio C++ build tools. This video walks you through the download and installation process.
In this lesson, we'll install Rust on Windows operating systems. The installation also includes the rustup tool for managing Rust and the cargo tool for managing projects.
Visual Studio Code (VSCode) is a free text editor for writing source code in various programming languages. In this lesson, we install the text editor and several extensions for Rust development.
Use the rustup tool to download the latest version of Rust, uninstall Rust, and access its documentation in your browser.
Git is a version control system for saving the checkpoints of a project. This lesson walks you through the basics of Git including installation.
The Cargo command-line tool creates a starter Rust project. In this lesson. we create a new project with a src/main.rs file. We also discuss the differences between binary crates and library crates.
Hello World is a rite of passage in the programming community; the first goal in any language is to output the text "Hello World" to the screen. In this lesson, we discuss the basic constructs of a Rust program that allow us to accomplish this goal: functions, blocks, the println! macro, parameters, semicolons, and more. We also learn how to use the rust-analyzer's Run button to compile and run the program directly from VSCode.
In this lesson, we compile our Rust project from the command-line using the cargo build command. We then run the executable which will be different between macOS and Windows operating systems.
Use the rustfmt and cargo fmt tools to format the Rust code in a single file vs. a whole Cargo project. Formatting does not change the structure of the code, only its aesthetics.
This lesson discusses the two modes of compilation that cargo build can utilize: debug and release. Debug is built for developers debugging while release is built for the final program/end user. We also learn the cargo clean command to delete the compiled executables.
Utilize the cargo run command to build and run the source code with a single command.
Use the cargo check command to check the source code for violations without compiling the program. The command tends to be faster because of the decreased amount of effort.
A comment is a line that is ignored by the Rust compiler. Developers use it to leave notes, descriptions, metadata, etc. In this lesson, we introduce the multiple ways to declare comments in Rust.
Test your knowledge of the section's concepts in a project.
See a solution to the previous lesson's project.
Download the course materials for the course from GitHub. You can either use the Git command line tools to fetch the content or download the ZIP file manually.
Review the concepts introduced in this section including the Rust compiler, the cargo CLI tool, the main function, comments, and more.
Open the project for this section and introduce the concepts we'll be learning!
A variable is a name assigned to a value in the program. In this lesson, we declare variables, use them in calculations, and observe Rust's data type inference.
Our strings can incorporate dynamic content with curly braces. This feature is called interpolation. In this lesson, we explore a variety of syntax options for injecting the content into our strings.
Pass positional arguments to the println! macro. Rust assigns each argument after the string a numeric position in line. The caveat is that the count starts at 0.
Use an underscore to inform the compiler that a variable is intentionally unused. We'll be encountering plenty of unused data throughout the course.
Variables are immutable by default, which means incapable of change. We need to use the mut keyword to mark each piece of data as mutable (capable of change).
Every Rust compiler error has a unique error code. In this lesson, we'll see how to find documentation for each error in both the Terminal and the online Rust error codes index.
Variable shadowing re-declares a variable, allowing us to reuse the same name with a different type. The most common usecase for variable shadowing is performing one or more data type transformations on a starter value; variable shadowing enables us to keep the same name for the data.
A scope is the boundary or region in which a name is valid. Scopes are connected to blocks. A block is the area between a pair of opening and curly braces. In this lesson, we trace the scope and existence of various variables declared in the main function.
A constant is name that we assign to a value. A constant can never change its value throughout the program. Its value and type must be known at compile time.
A type alias is a nickname assigned to an existing type. A type alias can provide some additional context on what a type represents in the program. We use the type keyword followed by the type name, an equal sign, and the original type.
A compiler directive is an annotation that instructs the compiler how to parse the code. In this lesson, we introduce the allow directive for permitting code that the compiler would otherwise express concern about. We learn how to declare directives for lines, functions, and files.
Test your knowledge of the section's concepts in a project.
See a solution to the previous lesson's project.
Review the concepts introduced in this section including variables, mutability, interpolation, error codes, scopes, constants, type aliases, and more!
Open the project for this section and introduce the concepts we'll be learning!
Dive into Rust's basic integer and floating point types. Understand the memory units of bits and the ranges of various data types.
Transition back into VSCode and practice annotating various integer types. See how the compiler alerts you to the limitations of a type.
Use the underscore character to visually separate the digits in an integer.
usize and isize are aliases for existing integer types in Rust. The type will depend on the architecture of the operating system that the executable is being run on. For example, the usize unsigned integer is equivalent to a u32 on a 32-bit system and a u64 on a 64-bit system.
A string is a piece of text. String literals are strings whose values the compiler knows at compile time. We declare strings in double quotes. In this lesson, we practice creating strings and introduce special characters like \n and \t. We also explore the benefits of raw strings.
A method is a function that lives on a value. We invoke a method with a dot, the method name, and a pair of parentheses. Methods may accept arguments.
In this lesson, we'll learn about Rust's two floating point types. Floats represent decimal numbers, numbers with a fractional component. Rust has two float types: f32 (32-bit) and f64 (64-bit). f64 offers double the precision of an f32.
Format the printed representation of a floating-point number using the format specifier (:) and the precision to output. The precision is the number of digits after the decimal point.
Utilize the as keyword to convert one numeric type into another. The technical word for this conversion is casting.
Explore the operators for common mathematical operations like addition, subtraction, division, multiplication, and remainder.
Augmented assignment operators are symbols that perform an operation on a variable's current value and overwrite the variable with the new value. For example, += adds a value and overwrites the variable with the sum.
A Boolean type can only be one of two values: true or false. It models a statement of truth. In this lesson, we declare some Booleans and explore some methods that return them.
Use the ! symbol to invert a Boolean. A true becomes false, and a false becomes ture.
Practice with the equality and inequality operators. The equality operator accepts two operands and checks whether they are equal. The inequality operator checks whether the two operands are not equal.
Use the && operator to validate that multiple Booleans evaluate to true. This models a scenario where multiple independent conditions must be met in order for something to be true.
Use the || operator to validate that either one of Booleans evaluate to true. This models a scenario where at least one of several independent conditions must be met in order for something to be true.
A character type represents a single Unicode character. It may represent common alphabetic characters in 100+ languages but can also represent an emoji. In this lesson, we explore some of the pitfalls of characters in programming.
An array is a fixed-size collection of homogenous data. Arrays store a sequence of values in order.
Let's declare some arrays and explore how the Rust compiler infers their types.
Rust assigns each element within an array an order in line. The count starts from 0. In this lesson, we learn how to both access and overwrite an element at an index position.
A trait is a contract that requires that a type support one or more methods. The Display trait requires a method on the type that will return the type as a user-friendly string. Every time we use {} interpolation syntax, we rely on the Display trait implementation on a type.
In this lesson, we introduce the complementary Debug trait. While Display is for human-readable output, the goal of Debug is to format a type into a programmer-facing string for debugging. We use the :? format specifier to print a value in Debug format.
In this lesson, we'll learn another macro called dbg!, which is short for "debug". The dbg! macro prints and returns the line of code we wrote and its output. It offers a nice shortcut to get the Display trait output of a value.
Like an array, a tuple is a collection type that can contain multiple elements, each of which is an assigned an index position reflecting its order in line. The difference between the two data types is that a tuple supports different types for the values. An array is homogenous, which means all of its elements must be of the same type. A tuple does not have that requirement, so it can store elements of different types.
The Range type represents a sequence or interval of consecutive values. For example, a range can represent the sequence of numbers between 15 and 23, or the lowercase characters between the letter 'b' and the letter 'h'.
A generic represents a type argument. A generic is a placeholder for a future type that has the potential to vary. Generics enable flexibility in the design of functions and types because the code is not bound to a single type.
Test your knowledge of the section's concepts in a project.
See a solution to the previous lesson's project.
Review the concepts introduced in this section including integers, floats, strings, tuples, arrays, booleans, methods, generics, the Display and Debug traits, and more!
Open the project for this section and introduce the concepts we'll be learning!
A function is a sequence of steps to be executed in order. It’s a procedure that encapsulates some logic. The power of functions lies in their ability to capture a reusable collection of instructions. In this lesson, we declare some simple functions and invoke them from main.
In this lesson, we'll learn how to define function parameters. A parameter is a name for an expected input. When a function is invoked, we are required to pass in a concrete value for that parameter, which is called an argument.
A return value is the output of a function. It is what the function gives back to the caller, which is what invoked the function in the first place. In this lesson, we use the return keyword to specify an explicit return value.
A Rust function implicitly returns the last evaluated value from its function body. In this lesson, we practice using this implicit return syntax.
This lesson introduces a special type called the unit. A unit is an empty tuple, a tuple without values. Functions in Rust return a unit if no return value is explicitly or implicitly specified.
In this lesson, we show to declare an evaluation block inside a function body. Rust evaluates the contents of the block and "returns" the last value it produces; its a similar concept to functions but one fully contained within a function body. The block creates an independent execution environment for isolating a collection of related code.
Test your knowledge of the section's concepts in a project.
See a solution to the previous lesson's project.
Review the concepts introduced in this section including functions, parameters and arguments, implicit and explicit return values, units, blocks, and more!
Open the project for this section and introduce the concepts we'll be learning!
The term control flow refers to how a program will execute. We are able to alter the execution of the program's logic based on conditionals. In this lesson, we introduce the if statement, which executes a block of code if a condition is met.
The else if acts as as second if statement that will be considered if the first if statement evaluates to false. We are able to check different related conditions to determine which block of code to execute.
The else statement offers a fallback option. If all previous if / else if statements evaluate to false, the program will execute the logic in the else block.
An if statement is an expression so we can assign the block's final value to a variable. In this lesson, we explore a few variations on this syntax.
In this lesson, we introduce one of Rust's most powerful features: the match statement. The match statement can react to every possible variant of a type. The compiler will not compile unless every scenario is handled. We practice applying match to a Boolean value.
The underscore character is a catch-all pattern that will always match. We talk about the advantages of _ in a match block as well as its potential pitfalls in this lesson.
A single match arm can check for multiple values by using vertical pipes or conditional logic. We explore all the wonderful possibilities in this lesson!
In this lesson, we'll introduce the loop keyword. The loop keyword declares a block of code that Rust will execute over and over. By default, there's no endpoint; we thus have to terminate the loop with the complementary break keyword.
The continue keyword terminates the current loop and starts the next loop. We can use it to avoid executing the remaining logic in the current iteration.
A while loop executes a block of code as long as a condition is met. It's a more declarative solution because Rust will handle the termination of the iteration. But we have to make sure to alter some part of state that the while loop will track and use to determine the end of iteration.
Recursion is when a function calls itself. In this lesson, we introduce this advanced programming concept and solve a sample problem with it.
Debugging is the process of finding and fixing errors in code. We can use VSCode's debugging feature to step through our code. We define breakpoints, which mark the lines where the editor will pause before execution. We can then observe the state of the program at that juncture.
When running the program in Debug mode, you can use the Watch panel to recompute an expression that is connected to a piece of data in the program. In this lesson, we practice by multiplying the seconds parameter from our recursive countdown function. We also discuss the limitations of the feature.
When we are paused at a breakpoint, the Debugging toolbar offers a few more navigation options:
The Step Over button executes a function without navigating through it's internals. It's equivalent to "stepping over" something in front of you.
The Step Into button navigates into a function/nested execution. If the line is not a function, Step Into will progress to the next line.
The Step Out button navigates outwards out of a function, completing its execution and returning you to the completion of the caller.
The Restart button restarts the Debugging from scratch, pausing at the first breakpoint.
Test your knowledge of the section's concepts in a project.
See a solution to the previous lesson's project.
Review the concepts introduced in this section including the if statement, else if, else, the match keyword, the loop and break keywords, and more!
Open the project for this section and introduce the concepts we'll be learning!
Ownership is a Rust compiler feature. Ownership is a set of rules the compiler checks for to make sure that, when the program runs, it will be free of a category of errors related to memory. Every value has a single owner; the owner is who is responsible for cleaning up the data when it goes out of scope.
The stack and the heap are two different parts or regions of the computer’s memory that are available to a Rust program when it runs. The stack is fast for reading and writing but can only store values whose size is known at compile time. The heap is slower but can store data of a dynamic size.
A scope is the boundary within a program where a name is valid. When the owner goes out of scope, it is responsible for cleaning up its data.
A trait is like a contract that a type promise to uphold. The Copy trait mandates that a type can be copied, which means that a full duplicate can be created from it. Rust's primitive data types, the ones stored on the stack, implement the Copy trait. We explore how this affects operations like variable assignment in this lesson.
In this lesson, we'll introduce the heap-based String data type. This is a distinct, separate type from the &str string slice. In this lesson, we learn two associated functions on the String type.
In this lesson, we use the push_str method to mutate a string and also describe what happens on the heap in that situation.
A move refers to the transfer of ownership from one owner to another. In this lesson, we see what happens when we assign a variable holding a String to another; the result varies depending on whether the type implements the Copy trait.
Rust has a function called drop that deallocates memory on the heap. We can invoke the drop function manually to invalidate a variable and deallocate its corresponding memory.
The ownership model in Rust exists to prevent common memory problems that are present in other languages. Another benefit of ownership is that it requires the programmer to explicitly state when a copy should be made of heap data. Rust wants to avoid making copies of heap data when possible To force a copy of existing heap data and avoid moving ownership, we can invoke the clone method on the original value.
A reference allows the program to use a value without moving its ownership. We describe this action of creating a reference as "borrowing". Just like in the real world, "borrowing" means we ask the owner to lend us something and then we promise to give it back to the owner when we're doing using it. We use the & operator to borrow a reference to a value.
In this lesson, we'll learn about the dereference operator, an asterisk. The * symbol follows a reference's memory address to the stored value. We also explore the situations where Rust automatically dereferences a reference for us.
In this review lesson, we draw the distinction between the two types of strings in Rust, str and String, as well as references to both of them.
Stack types implement the Copy trait. Rust will create full copies of the value when they are needed. That's because stack data is fixed in size and relatively cheap to copy.
A reference is still a type. It's an address. References implement the Copy trait too. If we assign a variable storing a reference to another variable, Rust will create a copy of the reference.
At this juncture, we’ve explored the concepts of copying vs moving when dealing with variable owners. The same ownership rules apply to functions and their parameters. A parameter takes ownership over the passed-in value if it does not implement the Copy trait.
Just like variables, function parameters are immutable by default. We cannot mutate the parameter's value within the function. We have to explicitly use the mut keyword to eclare whenever we want a parameter to be mutable.
Let's discuss the principles of ownership in a different direction, which is a function returning an owned value. A return value moves ownership out of an invoked function back to the caller.
In this lesson, we discuss the limitations of ownership. If we pass in an owned value as an argument to a function, we have to return it back to prevent deallocation. We'll see how references allow us to gracefully handle this situation in the next section!
Test your knowledge of the section's concepts in a project.
See a solution to the previous lesson's project.
Review the concepts introduced in this section including ownership, borrows and references, the dereference operator, moves, and more!
Open the project for this section and introduce the concepts we'll be learning!
In this lesson, we discuss how to declare immutable and mutable references. A mutable reference has permission to modify the original value. We can define function parameters to accept immutable ownership, mutable ownership, an immutable reference, or a mutable reference.
A value can have any number of simultaneous immutable references. There is no risk of the data changing, so all references can rely on stable data. We can have any number of readers but only one writer at a time.
If we have a single mutable reference, we cannot have any other reference to that same value at the same time, either mutable or immutable. A single mutable reference has the potential to mutate/change the value, which means other references' expectations about the data would prove invalid.
Immutable and mutable references follow different rules when it comes ownership and moves. Because we have any number of immutable references to the same value at the same time, it's safe to create a full copy of the immutable reference each time. However, ownership will move when dealing with mutable references.
A dangling reference is a pointer to an address in memory that has been deallocated. The compiler (more specifically, a feature called the borrow checker) watch out for dangling references. If a Rust program compiles, it will have no dangling references.
Ownership is not strictly limited to a name like a variable or parameter. Ownership is answering the question "Who's responsible for cleaning this data up"? Collection type owns their values. In this lesson, we'll learn more about the nuances of ownership with these compound types like arrays and tuples.
Test your knowledge of the section's concepts in a project.
See a solution to the previous lesson's project.
Review the concepts introduced in this section including immutable and mutable references, reference restrictions, dangling references, ownership with composite types, and more!
Open the project for this section and introduce the concepts we'll be learning!
A slice is a reference to a portion or sequence of a collection type. It's a subcategory of reference, it's a specific type of reference. Here's where things get nuanced. A portion is defined as "a part of the whole". In Rust, a slice can be any portion but technically that portion can represents the whole collection.
We can create a string slice from either one of Rust's two different string types, str and String. In this lesson, we introduce the square bracket and range syntax to borrow a slice of bytes from the string.
A string literal is a piece of text declared with double quotes. It is a string slice itself because it's a reference/borrow of the hardcoded text loaded into memory from the binary. In this lesson, we explore these ideas as we extract more slices from strings.
The length of a string slice refers to a count of its bytes, not a count of the number of characters. We can invoke the len method on a string slice to get its length.
In this lesson, I want to show you some syntactic shortcuts that we can utilize in certain slicing situations. We can omit the number before the colon to pull from the start. We can omit the number after the colon to pull to the end.
Next, let's explore how string slices and string references operate as function parameters. We also show how deref coercion allows us to define a more flexible type for a function's parameter.
We can create slices from a variety of different types, not just strings. For example, we can create an array slice, which is a reference to a portion or chunk of an array. We utilize the same borrowing syntax; we declare an ampersand to borrow a reference, then provide the array and a pair of square brackets.
Like with strings, we can declare a function's parameter as &[i32] to declare a slice type rather than a specific hardcoded array type with a fixed length. We see the benefits of this deref coercion feature in action in this lesson.
Rust does not permit mutable slices of strings. However, it does permit mutable slices of arrays. In this lesson, we borrow a mutable slice of an array portion and modify some of tis elements.
Test your knowledge of the section's concepts in a project.
See a solution to the previous lesson's project.
Review the concepts introduced in this section including string slices, array slices, deref coercion, syntactic shortcuts for slices, and more.
Open the project for this section and introduce the concepts we'll be learning!
A struct, which is short for the word structure, is a container for related data. A struct consists of fields, which are pieces of data with a name. In this lesson, we define a Coffee struct with 3 fields.
Now that we have a blueprint, let's create an instance! In this lesson, we instantiate our Coffee struct by providing concrete values for all expected fields.
To read the value of an field, we reference the struct, then provide a dot and the field's name. The fields are the owners of their values, so the regular rules of ownership apply. We have to be careful when accessing fields that do not implement the Copy trait because their ownership will move.
In this lesson, we'll learn how to overwrite the field values of a struct instance. We need to make the instance mutable first with the mut keyword. Then, we use the assignment operator (=) to assign a new value to the field.
Let’s define a new make_coffee function that returns a Coffee struct instance. Later on, we'll learn about associated functions, which is the more idiomatic way to create structs. But this example will demonstrate that structs can serve as return values.
In this lesson, I want to show you a shortcut we can use to simplify the creation of a struct instance. If a variable name or a parameter name matches a struct field, we can just write the field name once without any colon and Rust will connect it to the corresponding name.
In this lesson, we learn how to copy over either all or some of the fields from one struct to another. As always, principles of ownership apply.
A struct is a good function argument because it's a bundle of data. Instead of having to define multiple parameters, we can just define one parameter for the struct, and the struct will arrive as a single type containing many different fields. In this lesson, we practice the 4 different ways to pass in a struct to a function.
A trait is a contract that mandates that a type will support one or more methods. In this lesson, we use a derive attribute to get a Debug trait implementation for our struct. The default Debug representation includes the struct's name along with all fields and their values.
In this lesson, we'll learn how to define methods on a struct. A method is a function that belongs to a type, that lives on an instance. We introduce impl blocks and the syntax variations for accepting a struct instance as a method parameter.
In this lesson, we explore more options for how receiving the struct instance through the self parameter in a method definition.
Let's explore two more options for receiving the struct instance through the self parameter in a method definition. We can receive a reference to the struct; we can designate the reference as immutable (read-only) or mutable (having permission to mutate struct data).
Struct methods can define parameters just like regular functions can. The method must always accept some form of as self as its first parameter, but the parameters after self are to us. When we invoke a method, we do not need to pass in a value for self but do need to pass in arguments for the custom parameters.
A method is free to invoke another method on the instance. This is ideal if a method performs some kind of operation that can be shared or reused by other methods. Ideally, your methods should be small and have a single responsibility. In this lesson, we delegate the responsibility of figuring out the years of release of a TaylorSwiftSong to a separate method.
Associated functions are functions attached to a type. They are not methods; they do not live on an instance. The type creates a namespace. In this lesson, we introduce a category of associated functions called constructors and define a new function to return an instance of the TaylorSwiftSong struct.
A struct can define multiple impl blocks. Rust will merge all of the blocks' content into a single type definition. There’s no advantage to this multi-block syntax right now, but it will prove helpful for later concepts in the course.
In this lesson, we'll learn a design pattern called the builder pattern. A design pattern is a recommended way to write or structure your code to solve specific problems. In the builder pattern, every method returns the instance itself or, alternatively, a reference to the instance. The advantage of returning the value each time is that we can chain multiple methods in sequence in an elegant manner.
Rust has 3 kinds of structs: named-field, tuple-like, and unit-like. We've been playing around with the most common one: a named field struct, which assigns each piece of data a name. Let's now explore the other options, starting with a tuple-like struct, also called a tuple struct. A tuple struct is similar to a tuple. It's struct that stores multiple pieces of data, but each piece of data is not given a name. Rather, it is given a position in line.
Let's talk about unit-like structs, the third type of struct in Rust. As a reminder, a unit is a tuple without values; we declare it with an empty pair of parentheses for both the value and the type. We can similarly define a struct without any fields. These are called unit-like structs.
Test your knowledge of the section's concepts in a project.
See a solution to the previous lesson's project.
Review the concepts introduced in this section including structs, fields, methods, the self parameter, impl blocks, tuple structs, and more!
Open the project for this section and introduce the concepts we'll be learning!
An enum is a type that represents a set of possible values. Each possible value is called a variant. The word enum comes from the word "enumerate", which means “to mention one by one”. In this lesson, we declare a CardSuit enum and create a few instances of it.
An enum variant can store associated data. In this lesson, we declare a PaymentMethodType enum with multiple variants holding tuple variants.
One advantage of associated values is that different variants can have different associated data. In this lesson, we adjust our PaymentMethodType variants to store different amounts of data.
Rust will choose the enum's memory allocation based on what the largest variant requires. This enables a reassignment to a different enum variant without needing to move the memory to a different (larger) location.
In the previous lessons, we declared an enum variant without data and a tuple variant that stored data in sequential order. There's one more variant to discuss, which is a struct variant. The advantage of a struct variant over a tuple variant is that we can assign each piece of data a contextual name.
We can store an enum as data within an enum variant. In this lesson, we model a fine dining restaurant to practice these ideas!
The match keyword compares a given value against a collection of patterns, also called arms. It executes a block of code based on the first pattern that matches. The Rust compiler validates that the match handles all possible cases that a value could be. In this lesson, we apply the match statement to a custom enum.
In this lesson, we continue practicing with the match keyword. We expand our pattern arms to include both blocks and implicit return values.
If we have an enum variant with associated data, we can access the data in the corresponding match arm. The syntax will vary depending on whether we are dealing with a tuple variant or a struct variant.
Just like with structs, we can define methods on enums. We do so with the impl keyword followed by the enum name and a block. Our methods can take ownership of the enum or borrow a reference, either mutably or immutably.
In this lesson, we review the different ways we can match multiple values in a match arm for an enum.
In a match arm for an enum, we can match against a variant with a specific value. In this lesson, we expand our match pattern to target specific variants with specific data.
We'll often encounter scenarios where we only care about executing some conditional code if we have one specific enum variant. The disadvantage of the match keyword is that it is exhaustive; it covers every possible scenario. The if let construct enables us to match against one specific enum variant and execute conditional logic.
The let else construct allows us to do the opposite of an if let statement. It executes a block if a dynamic value does not match a hardcoded variant. In addition, it allows us to declare a variable that will live after that block and remain alive until it goes out of scope.
Test your knowledge of the section's concepts in a project.
See a solution to the previous lesson's project.
Review the concepts introduced in this section including enums, variants, associated data, tuple variants, struct variants, the match keyword, and the if let construct.
Learn to Code with Rust is a comprehensive introduction to programming in Rust, one of the fastest-growing programming languages in the world. Rust powers codebases in companies and products like Amazon, Microsoft, Firefox, Discord, and more. It is used in a variety of disciplines including web development, CLI programs, build tools, and databases.
Over more than 60 hours of video content, we'll tackle the language from A to Z, covering everything you need to know about Rust to be an effective developer.
The course is jam-packed with:
60+ hours of video, with new content added frequently
Multiple-choice quizzes
Coding challenges and projects
Section reviews
Learn to Code with Rust is designed from the ground up to take you from novice to professional. Complete beginners are welcome; no prior experience is needed! Over 400+ videos, we'll work our way from language fundamentals to advanced features. Topics covered include...
Setup & Installation
Variables
Data types
Functions and Methods
Control Flow
Ownership and References
Slices
Structs
Enums
Generics
Option and Result Enums
Vectors
Project Structure
Strings
Hash Haps
Error Handling
Traits
Lifetimes
Closures
Iterators
Testing
Randomness
...and more!
Throughout the entire journey, I'll be coding alongside you step by step in the code editor. You'll also be able to test your knowledge through numerous coding challenges, quizzes, and written assignments.
Rust is known to be a challenging language to learn. For many concepts, I had to browse through different books, articles, and videos to understand what was happening. My hope here is to demystify the concepts and make it easier for new students to learn the language.
Thanks for checking out the course!