Rust is a systems programming language that is extremely fast, prevents segmentation faults, and is thread-safe. While these features make Rust a powerful tool for systems programming, they also introduce some new concepts that people of other languages may not be familiar with.
In this comprehensive guide, "The Hard Things About Rust ," we aim to shed light on these challenging aspects of Rust and make them understandable to both novice and experienced programmers. We will shed light on these complex concepts and illustrate each concept with concrete examples and real-world scenarios for better understanding.
Here's what we'll cover:
-
Ownership : We'll start with the basic concept of ownership in Rust. We'll explore what it means to own a value, how ownership is transferred, and how Rust's ownership model helps with memory management.
-
Borrowing and Lifecycle : Building on the foundation of ownership, we'll delve into borrowing and lifecycle, two interrelated concepts that allow you to refer to data securely.
-
Slicing : We'll demystify slices, which are views of chunks of memory and are used extensively in Rust to access data efficiently.
-
Error handling : Rust's approach to errors is unique and robust. We'll introduce
Result
andOption
types, and how to use them for elegant error handling. -
Concurrency : We'll delve into Rust's powerful and sophisticated concurrency model. We'll discuss threads, message passing, and shared state concurrency, among others.
-
Advanced types and traits : We'll explore some of Rust's advanced types, such as
Box
,Rc
,Arc
. We'll also introduce Traits and Trait objects. -
Async/Await and Futures : As we move to advanced concepts, we'll explain Rust's async/await syntax and Futures model for handling asynchronous programming.
The goal of this guide is not just to provide an overview of these topics, but to help you understand the rationale behind these concepts, how they work behind the scenes, and how to use them effectively in your Rust programs.
Whether you're a Rust beginner looking to dive deeper into the language, or an intermediate Rustacean looking to solidify your understanding of these complex concepts, this guide is for you. Let's embark on this journey of conquering Rust's difficulties!
ownership
Ownership is a fundamental concept in Rust. It is part of Rust's approach to memory safety and makes Rust unique among programming languages. Understanding ownership is crucial to writing Rust programs, because many other Rust concepts, such as borrowing and lifetimes, are built on top of it.
What is ownership?
In Rust, every value has a variable called its owner . There can only be one owner at a time. The value will be deleted or cleaned up when the owner goes out of scope.
Let's consider a simple example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">s</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-string-color)">"hello world"</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-comment-color)">// s is the owner of the &str "hello world"</span>
<span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-comment-color)">// s goes out of scope here, and the string is dropped</span>
</code></span></span>
In the above code, the variable s
is the owner of the string "hello world"
. Once s
out of bounds at the end of the block, the string is deleted and its memory is freed.
mobile ownership
In Rust, assignment operators =
transfer ownership from one variable to another. =
This is different from other languages that copy values.
Consider this example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">s1</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"hello"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">s2</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">s1</span><span style="color:var(--syntax-text-color)">;</span>
</code></span></span>
In the above code, s1
string is initially owned "hello"
. However, the line let s2 = s1;
transfers ownership from s1
to s2
. Now, s2
is the owner of the string "hello"
and s1
is no longer valid. Rust will give you a compile-time error if you try to use after that s1
.
copy trait
Certain types in Rust implement this Copy
trait. When such a type is assigned to another variable, ownership is not moved, but a copy of the value is created. All integer and floating point types, boolean types, character types, and type tuples implementing traits Copy
are Copy
.
Here is an example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">x</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-literal-color)">5</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">y</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">;</span>
</code></span></span>
In the code above, x
is an integer, which implements the Copy
trait. So it doesn't move ownership when we write let y = x;
. Instead, it copies the value from x
to y
.
Why ownership?
The concept of ownership enables Rust to make memory safety guarantees without requiring a garbage collector. By enforcing that a value can only have one owner, and that the value is cleared when the owner goes out of scope, Rust prevents common programming errors such as null or dangling pointers, double frees, and data races.
Borrowing and lifetimes: safely referencing data in Rust
Borrowing and lifetimes are two of Rust's most notable features. Together, they enable Rust to be memory-safe and thread-safe without a garbage collector. Let's explore these concepts in detail.
loan
In Rust, we often let other parts of the code access a value without taking ownership of it. This is done through a feature called "borrowing". There are two types of borrowing: shared borrowing and mutable borrowing.
shared borrowing
Shared borrowing allows an item to have multiple references. &
This is done using symbols in Rust. Let's look at an example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">main</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">s1</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"hello"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">len</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">calculate_length</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">s1</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"The length of '{}' is {}."</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">s1</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">len</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">calculate_length</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">-></span> <span style="color:var(--syntax-text-color)">usize</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-name-color)">.len</span><span style="color:var(--syntax-text-color)">()</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
In this code, calculate_length
it is borrowed for s1
temporary use. s1
still belongs to the main
function, so we can s1
use it again after calling it calculate_length
.
mutable borrow
Mutable borrowing is when you want to allow changing the borrowed value. &mut
This is done by using in front of the variable. For example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">main</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-declaration-color)">mut</span> <span style="color:var(--syntax-text-color)">s1</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"hello"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">change</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-declaration-color)">mut</span> <span style="color:var(--syntax-text-color)">s1</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">change</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-declaration-color)">mut</span> <span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-name-color)">.push_str</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">", world"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
Here, change
the function is borrowing s1
and changing it. This is possible because of s1
mutable borrowing.
However, Rust has a rule that you can have one mutable reference or any number of immutable references, but not both. This rule guarantees that data races never occur.
Let's break down this concept with a code example.
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">main</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-declaration-color)">mut</span> <span style="color:var(--syntax-text-color)">s</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"hello"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">r1</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-comment-color)">// no problem</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">r2</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-comment-color)">// no problem</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"{} and {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">r1</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">r2</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-comment-color)">// r1 and r2 are no longer used after this point</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">r3</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-declaration-color)">mut</span> <span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-comment-color)">// no problem</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"{}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">r3</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
In this example, the code works fine because even though and are in scope r1
at creation time, they are not used r2
after creation . Rust's rules state (as stated earlier) that you can have one mutable reference or any number of immutable references, but not both. But this only applies when using references.r3
r3
Now, let's look at an example that violates Rust's borrowing rules:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">main</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-declaration-color)">mut</span> <span style="color:var(--syntax-text-color)">s</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"hello"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">r1</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-comment-color)">// no problem</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">r2</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-comment-color)">// no problem</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">r3</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-declaration-color)">mut</span> <span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-comment-color)">// PROBLEM! // cannot borrow `s` as mutable because it is also borrowed as immutable</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"{}, {}, and {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">r1</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">r2</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">r3</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
In this case we have r1
and , r2
which are immutable references and r3
which are mutable references. We're trying to use all of them at the same time, which violates Rust's borrowing rules, so the compiler will throw an error.
This rule prevents data races at compile time.
life
Lifecycles are Rust's way of ensuring that all borrows are valid. The point of lifecycles is to prevent dangling references. A dangling reference occurs when we have a reference to some data, and that data is removed before the reference.
In Rust, the compiler uses lifetimes to ensure that such errors do not occur. Here is an example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-text-color)">longest</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-text-color)">'a</span><span style="color:var(--syntax-error-color)">></span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">'a</span> <span style="color:var(--syntax-text-color)">str</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">y</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">'a</span> <span style="color:var(--syntax-text-color)">str</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">-></span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">'a</span> <span style="color:var(--syntax-text-color)">str</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-name-color)">.len</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-error-color)">></span> <span style="color:var(--syntax-text-color)">y</span><span style="color:var(--syntax-name-color)">.len</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">x</span>
<span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">else</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">y</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
In this function, 'a
is a lifetime parameter that says: For some lifetime 'a
, take two arguments, both of which are slices of strings at least as long as 'a
, and return a slice of strings at least as long as 'a
.
This is a bit abstract, let's consider a concrete example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">main</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">string1</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"long string is long"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">string2</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"xyz"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">result</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">longest</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">string1</span><span style="color:var(--syntax-name-color)">.as_str</span><span style="color:var(--syntax-text-color)">(),</span> <span style="color:var(--syntax-text-color)">string2</span><span style="color:var(--syntax-name-color)">.as_str</span><span style="color:var(--syntax-text-color)">());</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"The longest string is {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">result</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
Here, the lifetime of the is string1
longer than the lifetime of the string2
, so it won't be referenced when result
used in the , making sure we don't have dangling references.println!
string2
In summary, borrowing and lifetimes are two sides of the same coin that make Rust safe and efficient. They allow Rust to ensure safety and concurrency at compile time. Understanding them is key to mastering Rust.
Slicing: Sequence Views in Rust
Rust provides a way to refer to a contiguous sequence or part of a collection, rather than the entire collection itself. This is done with a feature called "slicing".
Understanding slices
A slice represents a reference to one or more contiguous elements of a collection, rather than a reference to the entire collection. Here's an example of a slice:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">main</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">s</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"hello world"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">hello</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-error-color)">..</span><span style="color:var(--syntax-literal-color)">5</span><span style="color:var(--syntax-text-color)">];</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">world</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-literal-color)">6</span><span style="color:var(--syntax-error-color)">..</span><span style="color:var(--syntax-literal-color)">11</span><span style="color:var(--syntax-text-color)">];</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"{} {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">hello</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">world</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
In this code, hello
and world
are slices s
. The numbers [0..5] and [6..11] are range indices that mean "start at index 0 and continue up to but not including index 5" and "start at index 6 and continue up to but not including index 11" respectively . If we run this program, it will print hello world
.
string slice
A string slice is a reference to a part of a string, it looks like this:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">s</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"hello world"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">hello</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-error-color)">..</span><span style="color:var(--syntax-literal-color)">5</span><span style="color:var(--syntax-text-color)">];</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">world</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-literal-color)">6</span><span style="color:var(--syntax-error-color)">..</span><span style="color:var(--syntax-literal-color)">11</span><span style="color:var(--syntax-text-color)">];</span>
</code></span></span>
Here hello
and world
are slices of strings s
. You can create slices by specifying [starting_index..ending_index] using ranges enclosed in brackets, where starting_index
is the first position in the slice and ending_index
one more than the last position in the slice.
array slice
Just like strings, we can also slice arrays. Here is an example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">main</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">a</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-literal-color)">2</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-literal-color)">3</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-literal-color)">4</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-literal-color)">5</span><span style="color:var(--syntax-text-color)">];</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">slice</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">a</span><span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-error-color)">..</span><span style="color:var(--syntax-literal-color)">3</span><span style="color:var(--syntax-text-color)">];</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"{:?}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">slice</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
Here, slice
will be a slice 2, 3
containing the second and third elements of the array a
.
The benefits of slicing
The power of slices is that they allow you to refer to contiguous sequences without copying the sequences into a new collection. This is a more efficient way of having functions access parts of a collection.
Error handling in Rust
Error handling is a fundamental part of any programming language, and Rust is no exception. It recognizes the inevitable bugs in software and provides powerful mechanisms to deal with them efficiently. The design of Rust's error handling mechanism requires developers to explicitly acknowledge and handle errors, making programs more robust and preventing many problems from affecting production environments.
Rust divides errors into two broad categories: recoverable errors and unrecoverable errors. Recoverable errors are usually the result of operations that would normally fail, such as attempting to open a file that does not exist. In such cases, we usually want to notify the user of the error and retry the operation or continue the program differently.
Unrecoverable errors, on the other hand, usually indicate a bug in your code, such as trying to access an array outside its bounds. These types of errors are severe enough to warrant stopping the program immediately.
Interestingly, Rust doesn't use exceptions, which is a common error handling mechanism in many languages. Instead, it provides two constructs: Result<T, E>
and panic!
macros, for handling recoverable and unrecoverable errors, respectively.
panic!
the macro
Macros in Rust panic!
are used to stop the execution of a program immediately. It's usually used when a program encounters a situation it doesn't know how to handle or when it reaches a state it should never have reached. These scenarios usually represent bugs in the program. When called panic!
, error messages are printed to standard error output, and the program terminates.
You can panic!
call it with a simple string message, or use it with a format string, something like . println!
The message you pass to will panic!
become the urgent payload and be returned as part of the error message when the program crashes. For example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-name-color)">panic!</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-name-color)">panic!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"this is a terrible mistake!"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">panic!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"this is a {} {message}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-string-color)">"fancy"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">message</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-string-color)">"message"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">std</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">panic</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">panic_any</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">4</span><span style="color:var(--syntax-text-color)">);</span> <span style="color:var(--syntax-comment-color)">// panic with the value of 4 to be collected elsewhere</span>
</code></span></span>
If panic!
called in the main thread, it will terminate all other threads and end your program with an exit code 101
.
Result<T, E>
enumerate
Rust's way of handling recoverable errors is encapsulated in enums Result<T, E>
. Result
is a generic enum with two variants: Ok(T)
for successful results, Err(E)
and for errors. The power of Result
is in its unambiguous nature; it forces the developer to handle both success and failure conditions, thus avoiding many common error-handling pitfalls.
Rust provides several Result
ways of manipulating values, the most famous of which are ?
operators. Operators ?
can be attached to return Result
. If the function succeeds and returns Ok(T)
, ?
the operator unwraps the value T
and the program continues. If the function encounters an error and returns Err(E)
, ?
the operator immediately returns from the current function and propagates the error up the call stack.
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">enum</span> <span style="color:var(--syntax-text-color)">Result</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-text-color)">T</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">E</span><span style="color:var(--syntax-error-color)">></span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Ok</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">T</span><span style="color:var(--syntax-text-color)">),</span>
<span style="color:var(--syntax-name-color)">Err</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">E</span><span style="color:var(--syntax-text-color)">),</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
This definition means that a function returning a Result
can succeed ( Ok
) and return a value of type T
, or fail ( Err
) and return an error of type E
.
Here is an example of a function that returns a Result
:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">use</span> <span style="color:var(--syntax-text-color)">std</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">num</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">ParseIntError</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">parse_number</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">str</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">-></span> <span style="color:var(--syntax-text-color)">Result</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-text-color)">i32</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">ParseIntError</span><span style="color:var(--syntax-error-color)">></span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">match</span> <span style="color:var(--syntax-text-color)">s</span><span style="color:var(--syntax-text-color)">.parse</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-text-color)">i32</span><span style="color:var(--syntax-error-color)">></span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Ok</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">n</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">=></span> <span style="color:var(--syntax-name-color)">Ok</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">n</span><span style="color:var(--syntax-text-color)">),</span>
<span style="color:var(--syntax-name-color)">Err</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">e</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">=></span> <span style="color:var(--syntax-name-color)">Err</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">e</span><span style="color:var(--syntax-text-color)">),</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">n</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">parse_number</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"42"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">match</span> <span style="color:var(--syntax-text-color)">n</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Ok</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">n</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">=></span> <span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"The number is {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">n</span><span style="color:var(--syntax-text-color)">),</span>
<span style="color:var(--syntax-name-color)">Err</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">e</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">=></span> <span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Error: {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">e</span><span style="color:var(--syntax-text-color)">),</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
In this example, parse_number
an attempt is made to parse a string into an integer. If successful, return the number inside Ok
, otherwise return the error inside Err
. There are two possible outcomes of this match
statement for processing Result
.
options
Enum Option
is similar to Result
but used when a function can return a value or not at all (instead of an error). It is defined as:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">enum</span> <span style="color:var(--syntax-text-color)">Option</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-text-color)">T</span><span style="color:var(--syntax-error-color)">></span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Some</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">T</span><span style="color:var(--syntax-text-color)">),</span>
<span style="color:var(--syntax-text-color)">None</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
Here's an example of a function that returns Option
:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">find</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">array</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-text-color)">i32</span><span style="color:var(--syntax-text-color)">],</span> <span style="color:var(--syntax-text-color)">target</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">i32</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">-></span> <span style="color:var(--syntax-text-color)">Option</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-text-color)">usize</span><span style="color:var(--syntax-error-color)">></span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">for</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">index</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">item</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-text-color)">array</span><span style="color:var(--syntax-name-color)">.iter</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-name-color)">.enumerate</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">item</span> <span style="color:var(--syntax-error-color)">==</span> <span style="color:var(--syntax-text-color)">target</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-name-color)">Some</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">index</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">None</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">array</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-literal-color)">2</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-literal-color)">3</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-literal-color)">4</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-literal-color)">5</span><span style="color:var(--syntax-text-color)">];</span>
<span style="color:var(--syntax-declaration-color)">match</span> <span style="color:var(--syntax-name-color)">find</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">array</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-literal-color)">3</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">Some</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">index</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">=></span> <span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Found at index {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">index</span><span style="color:var(--syntax-text-color)">),</span>
<span style="color:var(--syntax-text-color)">None</span> <span style="color:var(--syntax-declaration-color)">=></span> <span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Not found"</span><span style="color:var(--syntax-text-color)">),</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
In this example, the find
function tries to find a number in an array. If found, the function returns Some(index)
, where index
is the position of the number in the array. If not found, the function returns None
.
and Result
both Option
provide various useful methods for working with these types. For example, unwrap
can be used to get Ok
the value inside or , but Some
confusion occurs if is or the is . As a safer alternative, and can be used to provide default values or fallback functions, respectively.Result
Err
Option
None
unwrap_or
unwrap_or_else
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">x</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">Some</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">2</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">assert_eq!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-name-color)">.unwrap</span><span style="color:var(--syntax-text-color)">(),</span> <span style="color:var(--syntax-literal-color)">2</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">Option</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-text-color)">u32</span><span style="color:var(--syntax-error-color)">></span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">None</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-name-color)">assert_eq!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-name-color)">.unwrap_or</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">42</span><span style="color:var(--syntax-text-color)">),</span> <span style="color:var(--syntax-literal-color)">42</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">Result</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-text-color)">u32</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">str</span><span style="color:var(--syntax-error-color)">></span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">Err</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"emergency failure"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">assert_eq!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-name-color)">.unwrap_or_else</span><span style="color:var(--syntax-text-color)">(|</span><span style="color:var(--syntax-text-color)">_</span><span style="color:var(--syntax-text-color)">|</span> <span style="color:var(--syntax-literal-color)">42</span><span style="color:var(--syntax-text-color)">),</span> <span style="color:var(--syntax-literal-color)">42</span><span style="color:var(--syntax-text-color)">);</span>
</code></span></span>
In general, Result
and Option
are powerful tools in Rust for error handling and representing missing values. They make your code more explicit about possible failure or null conditions, helping to prevent many common programming errors.
concurrency research
Concurrency in Rust is achieved through several mechanisms, including threads, message passing, and shared state. Let's explore each of these in turn.
1. Thread
Rust has a std::thread
module that allows you to create new threads and use them in a system-independent manner. Here's a simple example of creating a new thread:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">use</span> <span style="color:var(--syntax-text-color)">std</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">thread</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">use</span> <span style="color:var(--syntax-text-color)">std</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">time</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">Duration</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">main</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">thread</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">spawn</span><span style="color:var(--syntax-text-color)">(||</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">for</span> <span style="color:var(--syntax-text-color)">i</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-error-color)">..</span><span style="color:var(--syntax-literal-color)">10</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"hi number {} from the spawned thread!"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">i</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">thread</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">sleep</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">Duration</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from_millis</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">));</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-declaration-color)">for</span> <span style="color:var(--syntax-text-color)">i</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-error-color)">..</span><span style="color:var(--syntax-literal-color)">5</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"hi number {} from the main thread!"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">i</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">thread</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">sleep</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">Duration</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from_millis</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">));</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
In this example, we create a new thread and thread::spawn
pass it a closure containing the new thread's instructions. The main thread and the new thread print their messages independently, sleeping for a millisecond between each message.
2. Messaging
Rust provides a message-passing concurrency model inspired by the Erlang language. Message passing is a method of dealing with concurrency in which threads or actors communicate by sending each other messages containing data.
In Rust, you can use this module to create channels std::sync::mpsc
(mpsc stands for multiple producers, single consumer). Here is an example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">use</span> <span style="color:var(--syntax-text-color)">std</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">sync</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">mpsc</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">use</span> <span style="color:var(--syntax-text-color)">std</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">thread</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">main</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">tx</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">rx</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">mpsc</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">channel</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">thread</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">spawn</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">move</span> <span style="color:var(--syntax-text-color)">||</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">val</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"hi"</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">tx</span><span style="color:var(--syntax-name-color)">.send</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">val</span><span style="color:var(--syntax-text-color)">)</span><span style="color:var(--syntax-name-color)">.unwrap</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">received</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">rx</span><span style="color:var(--syntax-name-color)">.recv</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-name-color)">.unwrap</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Got: {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">received</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
In this example, we create a channel with mpsc::channel
, and then move the transport ( tx
) into a new thread. This thread sends a message ("hi") to the channel, and we wait to receive that message in the main thread and print it.
3. Shared state
Rust also provides a way to share state between threads in a safe way using mutexes. A mutex provides mutual exclusion, meaning only one thread can access data at any given time. To access data, a thread must first signal that it wants access by asking the mutex to be locked. Here is an example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">use</span> <span style="color:var(--syntax-text-color)">std</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">sync</span><span style="color:var(--syntax-text-color)">::{</span><span style="color:var(--syntax-text-color)">Mutex</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">Arc</span><span style="color:var(--syntax-text-color)">};</span>
<span style="color:var(--syntax-declaration-color)">use</span> <span style="color:var(--syntax-text-color)">std</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">thread</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">main</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">counter</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Arc</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">new</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">Mutex</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">new</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-text-color)">));</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-declaration-color)">mut</span> <span style="color:var(--syntax-text-color)">handles</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">vec!</span><span style="color:var(--syntax-text-color)">[];</span>
<span style="color:var(--syntax-declaration-color)">for</span> <span style="color:var(--syntax-text-color)">_</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-error-color)">..</span><span style="color:var(--syntax-literal-color)">10</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">counter</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Arc</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">clone</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">counter</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">handle</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">thread</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">spawn</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">move</span> <span style="color:var(--syntax-text-color)">||</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-declaration-color)">mut</span> <span style="color:var(--syntax-text-color)">num</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">counter</span><span style="color:var(--syntax-name-color)">.lock</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-name-color)">.unwrap</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-error-color)">*</span><span style="color:var(--syntax-text-color)">num</span> <span style="color:var(--syntax-error-color)">+=</span> <span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-text-color)">handles</span><span style="color:var(--syntax-name-color)">.push</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">handle</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">for</span> <span style="color:var(--syntax-text-color)">handle</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-text-color)">handles</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">handle</span><span style="color:var(--syntax-name-color)">.join</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-name-color)">.unwrap</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Result: {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-error-color)">*</span><span style="color:var(--syntax-text-color)">counter</span><span style="color:var(--syntax-name-color)">.lock</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-name-color)">.unwrap</span><span style="color:var(--syntax-text-color)">());</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
In this example, we create a counter inside a mutex and then use atomic reference counting (Arc) to share it among multiple threads. Each thread locks the mutex, increments the counter, and releases the lock.
This is a high-level overview. Rust's concurrency model is very powerful and flexible, and it provides a number of features to ensure that concurrent code is free from data races and other common concurrency problems.
Advanced Types and Traits
Box
Box is a smart pointer that points to data stored on the heap, not the stack. They are useful when you have a large amount of data to store or you want to ensure that certain variables are not moved around in memory.
Boxes also have ownership. When the Box goes out of scope, the destructor is called and the heap memory is freed.
Here's a simple example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">b</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Box</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">new</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">5</span><span style="color:var(--syntax-text-color)">);</span> <span style="color:var(--syntax-comment-color)">// b is a pointer to a heap allocated integer</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"b = {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-error-color)">*</span><span style="color:var(--syntax-text-color)">b</span><span style="color:var(--syntax-text-color)">);</span> <span style="color:var(--syntax-comment-color)">// Output: b = 5</span>
</code></span></span>
In this example, the variable is a Box, which b
holds an integer on the heap. 5
operator *
is used to dereference a box, getting the value it points to.
RC
Rc stands for reference count. It's a smart pointer that allows multiple owners by keeping track of the number of references to the value that determines when to clean up. rc is used when we want to allocate some data on the heap to be read by multiple parts of the program, and we cannot determine at compile time which part used the data last.
It should be noted that Rc is only applicable to single-threaded scenarios. Here's a simple example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">use</span> <span style="color:var(--syntax-text-color)">std</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">rc</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">Rc</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">original</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Rc</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">new</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">5</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">a</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Rc</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">clone</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">original</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">b</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Rc</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">clone</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">original</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"original: {}, a: {}, b: {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-error-color)">*</span><span style="color:var(--syntax-text-color)">original</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-error-color)">*</span><span style="color:var(--syntax-text-color)">a</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-error-color)">*</span><span style="color:var(--syntax-text-color)">b</span><span style="color:var(--syntax-text-color)">);</span> <span style="color:var(--syntax-comment-color)">// Output: original: 5, a: 5, b: 5</span>
</code></span></span>
In this example, the variable is an Rc which original
holds an integer on the heap. 5
We can create multiple "clones" of this Rc (they are really just new pointers to the same data, not full copies). Heap memory will be freed when all Rcs go out of scope.
arc
Arc is atomic reference counting. It is the same as Rc, but is safe to use in a multithreaded context. It provides the same functionality as Rc, but uses atomic operations for reference counting. This makes it safe to share between multiple threads, at the cost of a slight performance hit.
Here is an example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">use</span> <span style="color:var(--syntax-text-color)">std</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">sync</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">Arc</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">use</span> <span style="color:var(--syntax-text-color)">std</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">thread</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">original</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Arc</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">new</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">5</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-declaration-color)">for</span> <span style="color:var(--syntax-text-color)">_</span> <span style="color:var(--syntax-declaration-color)">in</span> <span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-error-color)">..</span><span style="color:var(--syntax-literal-color)">10</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">original</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Arc</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">clone</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-text-color)">original</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">thread</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">spawn</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">move</span> <span style="color:var(--syntax-text-color)">||</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"{}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-error-color)">*</span><span style="color:var(--syntax-text-color)">original</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
In this example, we use Arc to share a heap-allocated integer among multiple threads. Each thread gets a clone of Arc (a new pointer to the data). When all Arcs go out of scope, the heap memory will be freed.
These types provide more advanced ways to manage memory and data ownership in Rust, enabling more complex data structures and patterns. However, they also add complexity and are harder to get right, so they should be used with caution.
trait
In Rust, a trait is a collection of methods defined for unknown types: Self
. They have access to other methods declared in the same trait and are a way to define shared or common behavior. Think of traits as a way of defining the interfaces a type can implement.
Consider this simple example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">trait</span> <span style="color:var(--syntax-text-color)">Animal</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">make_noise</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-declaration-color)">self</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">-></span> <span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">struct</span> <span style="color:var(--syntax-text-color)">Dog</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">struct</span> <span style="color:var(--syntax-text-color)">Cat</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">impl</span> <span style="color:var(--syntax-text-color)">Animal</span> <span style="color:var(--syntax-declaration-color)">for</span> <span style="color:var(--syntax-text-color)">Dog</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">make_noise</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-declaration-color)">self</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">-></span> <span style="color:var(--syntax-text-color)">String</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Woof!"</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">impl</span> <span style="color:var(--syntax-text-color)">Animal</span> <span style="color:var(--syntax-declaration-color)">for</span> <span style="color:var(--syntax-text-color)">Cat</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">make_noise</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-declaration-color)">self</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">-></span> <span style="color:var(--syntax-text-color)">String</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Meow!"</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
In the above example, we Animal
defined a Trait with method make_noise
. We then implement this trait for Dog
sum Cat
structures, providing their unique functional versions make_noise
. We can now Animal
call this function on any type that implements the trait.
Clone and Duplicate Features
Rust provides a number of predefined traits with specific behavior. Two of them are Clone
and Copy
features.
This Clone
feature allows explicit duplication of data. This method is called when you want to create a new copy of a type's data, clone
if the type implements that Clone
trait.
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-name-color)">#[derive(Clone)]</span>
<span style="color:var(--syntax-declaration-color)">struct</span> <span style="color:var(--syntax-text-color)">Point</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">i32</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">y</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">i32</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">p1</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Point</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">y</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">2</span> <span style="color:var(--syntax-text-color)">};</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">p2</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">p1</span><span style="color:var(--syntax-name-color)">.clone</span><span style="color:var(--syntax-text-color)">();</span> <span style="color:var(--syntax-comment-color)">// p1 is cloned into p2</span>
</code></span></span>
In this example, Point
structs implement the trait, so we can use this method Clone
to create a copy of any instance.Point
clone
On the other hand, this Copy
feature allows implicit duplication of data. It is used when we want to be able to make a shallow copy of a value without worrying about ownership. If the type implements the Copy
trait, the old variable is still available after assignment.
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-name-color)">#[derive(Copy,</span> <span style="color:var(--syntax-name-color)">Clone)]</span>
<span style="color:var(--syntax-declaration-color)">struct</span> <span style="color:var(--syntax-text-color)">Simple</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">a</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">i32</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">s1</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Simple</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-text-color)">a</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">10</span> <span style="color:var(--syntax-text-color)">};</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">s2</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">s1</span><span style="color:var(--syntax-text-color)">;</span> <span style="color:var(--syntax-comment-color)">// s1 is copied into s2</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"s1: {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">s1</span><span style="color:var(--syntax-text-color)">.a</span><span style="color:var(--syntax-text-color)">);</span> <span style="color:var(--syntax-comment-color)">// s1 is still usable</span>
</code></span></span>
In this example, the trait Simple
is implemented , allowing replication and still be available afterwards.Copy
s1
s2
However, be warned: not all types will work Copy
. Types that manage resources, such as String
custom structures that own heap data, cannot implement Copy
traits. In general, if a type requires some special operation when a value is deleted, then it cannot Copy
. This restriction prevents double free errors, which are a common problem in languages with manual memory management.
debug features
This Debug
feature can format the output structure data, which is usually used for debugging purposes. By default, Rust does not allow printing of struct values. However, once Debug
the trait is derived, you can use a macro println!
with debug format ( {:?}
) to print the struct value.
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-name-color)">#[derive(Debug)]</span>
<span style="color:var(--syntax-declaration-color)">struct</span> <span style="color:var(--syntax-text-color)">Rectangle</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">width</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">u32</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">height</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">u32</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">rect</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Rectangle</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-text-color)">width</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">30</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">height</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">50</span> <span style="color:var(--syntax-text-color)">};</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"rect is {:?}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">rect</span><span style="color:var(--syntax-text-color)">);</span>
</code></span></span>
In this example, a characteristic Rectangle
is derived Debug
, allowing you to print out its value on standard output.
PartialEq and Eq traits
This PartialEq
trait allows comparing instances of types for equality and inequality. This Eq
trait depends on PartialEq
, indicating that all comparisons are reflexive, i.e. if a == b
and b == c
, then a == c
.
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-name-color)">#[derive(PartialEq,</span> <span style="color:var(--syntax-name-color)">Eq)]</span>
<span style="color:var(--syntax-declaration-color)">struct</span> <span style="color:var(--syntax-text-color)">Point</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">i32</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">y</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">i32</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">p1</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Point</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">y</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">2</span> <span style="color:var(--syntax-text-color)">};</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">p2</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Point</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">y</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">2</span> <span style="color:var(--syntax-text-color)">};</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Are p1 and p2 equal? {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">p1</span> <span style="color:var(--syntax-error-color)">==</span> <span style="color:var(--syntax-text-color)">p2</span><span style="color:var(--syntax-text-color)">);</span>
</code></span></span>
In this example, the and traits Point
are derived so that instances can be compared.PartialEq
Eq
Point
PartialOrd and Ord traits
These traits support comparison operations ( <
, >
, <=
, >=
) on type instances. PartialOrd
Partial ordering is allowed, where some values may not be comparable. On the other hand, Ord
a complete ordering between values can be achieved.
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-name-color)">#[derive(PartialOrd,</span> <span style="color:var(--syntax-name-color)">Ord,</span> <span style="color:var(--syntax-name-color)">PartialEq,</span> <span style="color:var(--syntax-name-color)">Eq)]</span>
<span style="color:var(--syntax-declaration-color)">struct</span> <span style="color:var(--syntax-text-color)">Point</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">i32</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">p1</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Point</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">1</span> <span style="color:var(--syntax-text-color)">};</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">p2</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Point</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-literal-color)">2</span> <span style="color:var(--syntax-text-color)">};</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Is p1 less than p2? {}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">p1</span> <span style="color:var(--syntax-error-color)"><</span> <span style="color:var(--syntax-text-color)">p2</span><span style="color:var(--syntax-text-color)">);</span>
</code></span></span>
In this example, the , , and features Point
are exported . This allows comparing instances.PartialOrd
Ord
PartialEq
Eq
Point
default feature
This Default
trait allows the creation of default values for types. default
It provides a function that returns the default value of the type.
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-name-color)">#[derive(Default)]</span>
<span style="color:var(--syntax-declaration-color)">struct</span> <span style="color:var(--syntax-text-color)">Point</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">x</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">i32</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">y</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">i32</span><span style="color:var(--syntax-text-color)">,</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">p1</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">Point</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">default</span><span style="color:var(--syntax-text-color)">();</span> <span style="color:var(--syntax-comment-color)">// Creates a Point with x and y set to 0</span>
</code></span></span>
In this example, features Point
are exported Default
. This allows to create Point
an instance with a default value (0 in this case).
async/await and futures
futures
An A in Rust Future
means a value that may not yet be computed. They are a concept in concurrent programming that enable non-blocking computations: the program can continue with other tasks instead of waiting for slow computations to complete.
Futures are based on Future
traits, and their simplest form looks like this:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">pub</span> <span style="color:var(--syntax-declaration-color)">trait</span> <span style="color:var(--syntax-text-color)">Future</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">type</span> <span style="color:var(--syntax-text-color)">Output</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">poll</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">self</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">Pin</span><span style="color:var(--syntax-error-color)"><&</span><span style="color:var(--syntax-declaration-color)">mut</span> <span style="color:var(--syntax-declaration-color)">Self</span><span style="color:var(--syntax-error-color)">></span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">cx</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-error-color)">&</span><span style="color:var(--syntax-declaration-color)">mut</span> <span style="color:var(--syntax-text-color)">Context</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">-></span> <span style="color:var(--syntax-text-color)">Poll</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-declaration-color)">Self</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">Output</span><span style="color:var(--syntax-error-color)">></span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
This Future
trait is the asynchronous version of Generator
. It has a method that poll
is called by the executor to push the future to completion. The poll
method checks whether Future
its calculations have completed. If so, return it Poll::Ready(result)
. If not, it returns Poll::Pending
and arranges for the current task poll
to be notified when it should be called again.
async and await
async
And await
are used in Rust for processing Futures
. You can think of it as async
a way of creating Future
, and await
a way of consuming Future
.
async
is a keyword that you can put in front of a function to make it return one Future
. Here's a simple async function:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">compute</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-declaration-color)">-></span> <span style="color:var(--syntax-text-color)">i32</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-literal-color)">5</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
compute
It will return a when you call it Future
, and will yield a value when the driver is done 5
.
await
is a Future
way to suspend the execution of the current function until a completes. Here is an example:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">compute_and_double</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-declaration-color)">-></span> <span style="color:var(--syntax-text-color)">i32</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">value</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">compute</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-declaration-color)">.await</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-text-color)">value</span> <span style="color:var(--syntax-error-color)">*</span> <span style="color:var(--syntax-literal-color)">2</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
Here, compute().await
execution is paused compute_and_double
until it compute
finishes running. Once compute
complete, its return value is used to resume the compute_and_double
function.
While a function is suspended await
, the executor can run other functions Futures
. This is how asynchronous programming in Rust achieves high concurrency: by running multiple tasks concurrently, and switching between them while the tasks wait for slow operations such as I/O.
Executor
Executors are responsible for driving tasks Future
to completion. Describes Future
what needs to happen, but the Executor's job is to make it happen. In other words, without an executor, Futures
nothing can be done.
block_on
Here's a simple example using the executor from the crate futures
:
<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">use</span> <span style="color:var(--syntax-text-color)">futures</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">executor</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-text-color)">block_on</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">hello</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-declaration-color)">-></span> <span style="color:var(--syntax-text-color)">String</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-text-color)">String</span><span style="color:var(--syntax-text-color)">::</span><span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"Hello, world!"</span><span style="color:var(--syntax-text-color)">)</span>
<span style="color:var(--syntax-text-color)">}</span>
<span style="color:var(--syntax-declaration-color)">fn</span> <span style="color:var(--syntax-name-color)">main</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">future</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">hello</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-text-color)">result</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">block_on</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">future</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-name-color)">println!</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"{}"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">result</span><span style="color:var(--syntax-text-color)">);</span>
<span style="color:var(--syntax-text-color)">}</span>
</code></span></span>
In this example, block_on
a is taken Future
and the current thread is blocked until a Future
completes. Then it returns Future
the result.
There are many different executors available in Rust, each with different characteristics. Some, for example tokio
, are designed for building high-performance web services. Others, for example async-std
, provide a set of asynchronous utilities that feel like the standard library.
Remember, as the developer, it's your responsibility to ensure that Futures
the executors push to completion properly. If a Future
is discarded without completing or driving to completion awaited
, it doesn't have a chance to clean itself up.
Taken together, Rust's async/await
syntax and Future
features provide a powerful model for writing asynchronous code. However, they are also complex and require a good understanding of the language's ownership and concurrency models.
In summary,
Rust provides a powerful toolset for complex programming tasks, providing unparalleled control over system resources. It contains high-level types, traits, and asynchronous functions, and meets both low-level and high-level programming needs. While Rust might seem intimidating at first, the benefits it offers in terms of performance, control, and safety make the learning journey worthwhile. Understanding the concepts of ownership, borrowing, and lifetime will be your guide in navigating Rust's complexities. By embracing these principles, you'll be equipped to tackle the most challenging aspects of Rust programming. Happy coding!