Compare commits

...

13 Commits

Author SHA1 Message Date
732235ab69 finsihed The Rust Programming Language Bookgit add -Agit add -A
All checks were successful
Test Gitea Actions / first (push) Successful in 21s
Test Gitea Actions / check-code (push) Successful in 17s
Test Gitea Actions / test (push) Successful in 17s
Test Gitea Actions / documentation-check (push) Successful in 16s
2025-04-18 16:14:55 -06:00
ff4c4fccee finished ch20.5 and ch20 WHOOH
All checks were successful
Test Gitea Actions / first (push) Successful in 20s
Test Gitea Actions / check-code (push) Successful in 16s
Test Gitea Actions / test (push) Successful in 16s
Test Gitea Actions / documentation-check (push) Successful in 17s
2025-04-17 16:09:28 -06:00
4274106509 started ch20.5
All checks were successful
Test Gitea Actions / first (push) Successful in 20s
Test Gitea Actions / check-code (push) Successful in 16s
Test Gitea Actions / test (push) Successful in 16s
Test Gitea Actions / documentation-check (push) Successful in 16s
2025-04-17 12:50:58 -06:00
e6bb4977b8 almost finished ch20.4
All checks were successful
Test Gitea Actions / first (push) Successful in 21s
Test Gitea Actions / check-code (push) Successful in 16s
Test Gitea Actions / test (push) Successful in 19s
Test Gitea Actions / documentation-check (push) Successful in 19s
2025-04-16 17:13:02 -06:00
e73197aa26 finished ch20.3 2025-04-16 15:44:05 -06:00
0c0dcfee81 finished ch20.2 2025-04-15 17:03:30 -06:00
5da2a5c2b5 finished ch20.1
All checks were successful
Test Gitea Actions / first (push) Successful in 19s
Test Gitea Actions / check-code (push) Successful in 15s
Test Gitea Actions / test (push) Successful in 16s
Test Gitea Actions / documentation-check (push) Successful in 15s
2025-04-15 12:33:04 -06:00
6aa4deab15 started ch20.1
All checks were successful
Test Gitea Actions / first (push) Successful in 32s
Test Gitea Actions / check-code (push) Successful in 15s
Test Gitea Actions / test (push) Successful in 15s
Test Gitea Actions / documentation-check (push) Successful in 15s
2025-04-14 17:18:50 -06:00
848e5c4e33 finished ch20 intro
All checks were successful
Test Gitea Actions / first (push) Successful in 20s
Test Gitea Actions / check-code (push) Successful in 18s
Test Gitea Actions / test (push) Successful in 18s
Test Gitea Actions / documentation-check (push) Successful in 17s
2025-04-11 11:26:46 -06:00
295d603cbd finished ch19.3 and ch19
All checks were successful
Test Gitea Actions / first (push) Successful in 16s
Test Gitea Actions / check-code (push) Successful in 16s
Test Gitea Actions / test (push) Successful in 16s
Test Gitea Actions / documentation-check (push) Successful in 15s
2025-04-10 16:47:49 -06:00
730b0091e8 finished ch19.2 and started ch19.3
All checks were successful
Test Gitea Actions / first (push) Successful in 21s
Test Gitea Actions / check-code (push) Successful in 18s
Test Gitea Actions / test (push) Successful in 18s
Test Gitea Actions / documentation-check (push) Successful in 17s
2025-04-09 17:00:42 -06:00
f0cbfdf024 finished ch19.1
All checks were successful
Test Gitea Actions / first (push) Successful in 17s
Test Gitea Actions / check-code (push) Successful in 17s
Test Gitea Actions / test (push) Successful in 18s
Test Gitea Actions / documentation-check (push) Successful in 18s
2025-04-08 16:00:21 -06:00
c3ea7d26e3 finished ch19 intro 2025-04-08 11:54:40 -06:00
22 changed files with 3510 additions and 28 deletions

View File

@ -1,5 +1,5 @@
{
"collapse-filter": true,
"collapse-filter": false,
"search": "",
"showTags": false,
"showAttachments": false,
@ -18,5 +18,5 @@
"linkStrength": 1,
"linkDistance": 250,
"scale": 0.8410178518902366,
"close": false
"close": true
}

View File

@ -8,45 +8,73 @@
"type": "tabs",
"children": [
{
"id": "b49e674e0ebaaeb7",
"id": "de2ac5df5b921166",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Trait Objects that allow for Values of Different Types.md",
"file": "Advanced Features.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Trait Objects that allow for Values of Different Types"
"title": "Advanced Features"
}
},
{
"id": "f9ef446f856cead7",
"id": "0cdb5cb65c12cd29",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Implementing OO Design Pattern.md",
"file": "Macros.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Implementing OO Design Pattern"
"title": "Macros"
}
},
{
"id": "629d55df46f486d8",
"id": "8efdb57e394f2650",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Pattern Matching.md",
"file": "Advanced Traits.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Pattern Matching"
"title": "Advanced Traits"
}
},
{
"id": "74db89a42def0b8b",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Advanced Types.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Advanced Types"
}
},
{
"id": "8cdde5c4be386d20",
"type": "leaf",
"state": {
"type": "markdown",
"state": {
"file": "Unsafe Rust.md",
"mode": "source",
"source": false
},
"icon": "lucide-file",
"title": "Unsafe Rust"
}
},
{
@ -59,8 +87,7 @@
"title": "Graph view"
}
}
],
"currentTab": 2
]
}
],
"direction": "vertical"
@ -203,10 +230,20 @@
"command-palette:Open command palette": false
}
},
"active": "629d55df46f486d8",
"active": "de2ac5df5b921166",
"lastOpenFiles": [
"Implementing OO Design Pattern.md",
"Macros.md",
"Advanced Features.md",
"Advanced Functions and Closures.md",
"Advanced Traits.md",
"Advanced Types.md",
"Enums.md",
"Unsafe Rust.md",
"Pattern Matching.md",
"Places Patterns Can Be Used.md",
"Pattern Syntax.md",
"Refutability.md",
"Implementing OO Design Pattern.md",
"Trait Objects that allow for Values of Different Types.md",
"Characteristics of OO Languages.md",
"OOP Programming Features.md",
@ -221,16 +258,6 @@
"Sync and Send.md",
"().md",
"Smart Pointers.md",
"Simultaneous Code Running.md",
"Passing Data Between Threads.md",
"Leaky Reference Cycles.md",
"Test Controls.md",
"Ref Cell Mutability.md",
"Reference Counter Smart Pointer.md",
"The Performance Closures and Iterators.md",
"Improving The IO Project.md",
"Tests.md",
"The Preformance Closures and Iterators.md",
"minigrep/src/lib.rs",
"does_not_compile.svg",
"Untitled.canvas",

26
Advanced Features.md Normal file
View File

@ -0,0 +1,26 @@
# Advanced Features
So far we learned about the most commonly used parts of the Rust programming language.
Here we will look at a few aspects of the language you may run into once in a while but may not use everyday.
This is intended to be used as a reference for when you encounter any unknowns.
The features covered here are useful in very specific situations.
In this chapter we will cover
- [Unsafe Rust](./Unsafe%20Rust.md): How to opt out of some of Rust's guarantees and take responsibility for manually upholding those guarantees
- [Advanced traits](./Advanced%20Traits.md): associated types, default type parameters, fully qualified syntax, supertraits, and the new type pattern in relation to traits
- [Advanced types](./Advanced%20Types.md): more about the newtype pattern, type aliases, the never type, and dynamically sized types
- [Advanced functions and closures](./Advanced%20Functions%20and%20Closures.md): function pointers and returning closures
- [Macros](./Macros.md): ways to define code that defines more code at compile time
## Summary
These are great tools to have in your toolbox that you like likely not use often.
Knowing that they are available is very valuable.
The complex topics introduced are useful for when you encounter them in error messages in other people's code.
So you will be able to recognize the concepts and syntax.
Use this chapter as a reference to guide you solutions.

View File

@ -0,0 +1,145 @@
# Advanced Functions and Closures
This section includes some more advanced features including function pointers and returning closures.
## Function Pointers
We have talked about how to pass closures to functions; you can also pass regular functions to functions.
This technique is useful when you want to pass a function that you have already defined rather than defining a new closure.
Functions coerce to the type `fn` (with a lowercase f), not to be confused with, not to be confused with the `Fn` closure trait.
The `fn` type is called a *function pointer*.
Passing function pointers will allow you to use functions as arguments to other functions.
The syntax for specifying that a parameter is a function pointer is similar to that of closures as shown below.
This shows that we have defined a function `add_one` that adds one to its parameter.
The function `do_twice` takes two parameters: a function pointer to any function takes an `i32` parameter and returns an `i32`, and one `i32` value.
The `do_twice` function calls the function `f` twice, passing it the `arg` value, then adds the two function call results together.
The `main` function calls `do_twice` with the arguments `add_one` and `5`.
```rust
fn add_one(x: i32) -> i32 {
x + 1
}
fn do_twice(f: fn(i32) -> i32, arg: i32) -> i32 {
f(arg) + f(arg)
}
fn main() {
let answer = do_twice(add_one, 5);
println!("The answer is: {answer}");
}
```
Output
```
The answer is: 12
```
Here we specify that the parameter `f` in `do_twice` is an `fn` that takes one parameter of type `i32` and returns a `i32`.
We can then call `f` from the body of `do_twice`.
In `main` we can pass the function name `add_one` as the first arg to `do_twice`.
Unlike closures, `fn` is a type rather than a trait, so we need to specify `fn` as the parameter type directly rather than declaring a generic type parameter with one of the `Fn` traits as a trait bound.
Function pointers implement all three of the closure traits (`Fn`, `FnMut` and `FnOnce`).
This means that you can always pass a function pointer as an argument for a function that expects a closure.
It is best to write functions using a generic type and one of the closure traits so your functions can accept either functions or closures.
That being said, one example of where you would only want to accept `fn` and not closures is when interfacing with external code that doesn't have closures.
C functions can accept functions as arguments, but C doesn't have closures.
As an example of where you could use either a closure defined inline or a named function.
Lets look at a use of the `map` method provided by the `Iterator` trait in the std library.
To use the `map` function to turn a vector of numbers into a vector of strings, we could use a closure.
Like this:
```rust
let list_of_numbers = vec![1, 2, 3];
let list_of_strings: Vec<String> =
list_of_numbers.iter().map(|i| i.to_string()).collect();
```
We could have a function as the argument to `map` instead of the closure.
Like this:
```rust
let list_of_numbers = vec![1, 2, 3];
let list_of_strings: Vec<String> =
list_of_numbers.iter().map(ToString::to_string).collect();
```
Note, we must use the fully qualified syntax that we talked about earlier in ["Advanced Traits"](./Advanced%20Traits.md) section.
This is because there are multiple functions available named `to_string`.
Here we are using the `to_string` function defined in the `ToString` trait, which is in the std library has implemented for any type that implements `Display`.
Recall form the ["Enum values"]() section of Ch6 that the name of each enum variant that we define also becomes an initializer function.
We can use these initializer functions as function pointers that implement the closure traits, this means we can specify the initializer functions as arguments for methods that take closures.
Like this:
```rust
enum Status {
Value(u32),
Stop,
}
let list_of_statuses: Vec<Status> = (0u32..20).map(Status::Value).collect();
```
Here we creates `Status::Value` instances using each `u32` value in the range that `map` is called on by using the initializer function of `Status::Value`.
Some prefer to use this style, and some people prefer to use closures.
They compile to the same code, so use whatever style is clearer to you.
## Returning Closures
Closures are represented by traits, meaning that you can't return closures directly.
In cases where you want to return a trait, you instead use the concrete type that implements the trait as the return value of the function.
You are unable to do that with closures because they do not have a concrete type that is returnable.
You are not allowed to use the function pointer `fn` as a return type, for example.
Instead, you would normally use the `impl Trait` syntax we learned from Ch10.
You can return any function type using the `Fn`, `FnOnce` and `FnMut`.
This code would work just fine.
```rust
fn returns_closure() -> impl Fn(i32) -> i32 {
|x| x + 1
}
```
As noted in the ["Closure Type Inference and Annotation"](./Closures.md#closure-type-inference-and-annoation) section in Ch13, each closure is also its own distinct type.
If you needed to work with multiple functions that have the same signature but slightly different implementations, you will need to use a trait object for them.
```rust
fn main() {
let handlers = vec![returns_closure(), returns_initialized_closure(123)];
for handler in handlers {
let output = handler(5);
println!("{output}");
}
}
fn returns_closure() -> Box<dyn Fn(i32) -> i32> {
Box::new(|x| x + 1)
}
fn returns_initialized_closure(init: i32) -> Box<dyn Fn(i32) -> i32> {
Box::new(move |x| x + init)
}
```
This will compile just fine, but it wouldn't if we had tried to stick with `impl Fn(i32) -> i32`.

542
Advanced Traits.md Normal file
View File

@ -0,0 +1,542 @@
# Advanced Traits
Here we will go into the nitty-gritty of traits..
## Specifying Placeholder Types in Trait Definitions with Associated Types
*Associated types* connect a type placeholder with a trait such that the trait method definitions can use these placeholder types in their signatures.
The implementor of a trait will specify the concrete type to be used instead of the placeholder type for the particular implementation.
This way we can define a trait that uses some types without needing to know exactly what those types are until the trait is implemented.
We have described most of the advanced features in this chapter as being rarely needed.
Associated types are somewhere in the middle: they are used more rarely than features explained in the rest of the book, but more commonly than many of the other features discussed in this chapter.
One example of trait with an associated type is the `Iterator` trait that the std library provides.
The associated type is named `Item` and stands in for the type of the values the type implementing the `Iterator` trait is iterating over.
Here is the definition of the `Iterator` trait.
```rust
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
```
The type `Item` is a placeholder, and the `next` method's definition shows that it will return values of type `Option<Self::Item>`.
Implementors of the `Iterator` trait will specify the concrete type for `Item` and the `next` method will return an `Option` containing a value of that concrete type.
Associated types may seem like a similar concept to generics, in that the latter allow us to define a function without specifying what types it can handle.
To examine the difference between the two, we will look at the implementation of the `Iterator` trait on a type named `Counter` that specifies the `Item` type is `u32`.
```rust
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
// --snip--
```
This syntax seems similar to that of generics.
So why not just define the `Iterator` trait with generics
```rust
pub trait Iterator<T> {
fn next(&mut self) -> Option<T>;
}
```
The differences is that when using generics, we must annotate the types in each implementation.
Because we can also implement `Iterator<String> for Counter` or any other type, we could have multiple implementations of `Iterator` for `Counter`.
This could also be said, when a trait has a generic parameter, it can be implemented for a type multiple times, changing the concrete types of the generic type parameters each time.
When we use the `next` method on `Counter`, we would have to provide type annotations to indicate which implementation of `Iterator` we want to use.
With associated types, we don't need to annotate types because we can't implement a trait on a type multiple times.
In the first definition that uses associated types, we can only choose what the type of `Item` will be once because there can only be one `impl Iterator for Counter`.
We don't have to specify that we want an iterator of `u32` values everywhere that we call `next` on `Counter`.
Associated types also become part of the trait's contract.
Implementors of the trait must also provide a type to stand in for the associated type placeholder.
Associated types often have a name that describes how the type will be used, and documenting the associated type in the API documentation is good practice.
## Default Generic Type Parameters and Operator Overloading
When we use generic type parameters, we can specify a default concrete type for the generic type.
This eliminates the need for implementors of the trait to specify a concrete type if the default type works.
You can specify a default type when declaring a generic type with the `<PlaceholderType=ConcreteType>` syntax.
A good example of a situation where this technique is useful is with *operator overloading*, where you customize the behavior of an operator (such as `+`) in particular situations.
Rust doesn't allow you to create your own operators or overload arbitrary operators.
You can overload the operation and corresponding traits listed in `std::ops` by implementing the traits associated with the operator.
For example here we overload the `+` operator to add two `Point` instances together.
We do this by implementing the `Add` trait on a `Point` struct.
```rust
use std::ops::Add;
#[derive(Debug, Copy, Clone, PartialEq)]
struct Point {
x: i32,
y: i32,
}
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
fn main() {
assert_eq!(
Point { x: 1, y: 0 } + Point { x: 2, y: 3 },
Point { x: 3, y: 3 }
);
}
```
The `add` method adds the `x` values of two `Point` instances and the `y` values of two `Point` instances to create a new `Point`.
The `Add` trait has an associated type named `Output` that determines the type returned from the `add` method.
The default generic type in this code is within the `Add` trait.
Here is its definition
```rust
trait Add<Rhs=Self> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
}
```
This should look generally familiar: a trait with one method and an associated type.
The new part is `Rhs=Self`: this syntax is called *default type parameters*.
The `Rhs` generic type parameter (short for "right hand side") defines the type of the `rhs` parameter in the `add` method.
If we didn't specify a concrete type for `Rhs` when we implement the `Add` trait, the type of `Rhs` will default to `Self`, which will be the type we are implementing `Add` on.
When we implemented `Add` for `Point`, we used the default for `Rhs` because we wanted to add two `Point` instances.
Now lets look at an example of implementing the `Add` trait where we want to customize the `Rhs` type rather than using the default.
Here we want two structs `Millimeters` and `MEters`, which hold values in different units.
This thin wrapping of an existing type in another struct is known as the *newtype pattern*, which will be described in more detail in the ["Using the Newtype Pattern to Implement External Traits on External Types"]() section.
We want to add values in millimeters to values in meters and have the implementation of `Add` do the conversion correctly.
We can implement `Add` for `Millimeters` with `Meters` as the `Rhs`.
```rust
use std::ops::Add;
struct Millimeters(u32);
struct Meters(u32);
impl Add<Meters> for Millimeters {
type Output = Millimeters;
fn add(self, other: Meters) -> Millimeters {
Millimeters(self.0 + (other.0 * 1000))
}
}
```
To add `Millimeters` and `Meters`, we specify `impl Add<Meters>` to set the value of the `Rhs` type parameter instead of using the default of `Self`.
You will use default type parameters in two main ways:
- To extend a type without breaking existing code
- To allow customization in specific cases most users won't need
The std library's `Add` trait is an example of the second purpose.
Usually, you will add two like types, but the `Add` trait provides the ability to customize beyond that.
Using a default type parameter in the `Add` trait definition means you don't have to specify the extra parameter most of the time.
A bit of implementation boilerplate isn't needed, making it easier to use the trait.
The first purpose is similar to the second but in reverse.
If you want to add a type parameter to an existing trait, you can give it a default to allow extension of the functionality of the trait without breaking the existing implementation code.
## Fully Qualified Syntax for Disambiguation: Calling Methods with the Same Name
Nothing in Rust prevents a trait from having a method with the same name as another trait's methods.
Nor does Rust prevent you from implementing both traits on one type.
It is also possible to implement a method directly on the type with the same name as methods form traits.
When calling methods with the same name, you need to specify to Rust which one you want to use.
Consider this code where we have defined two traits, `Pilot` and `Wizard`, that both have a method called `fly`.
Then implement both traits on a type `Human` that already has a method named `fly` implemented on it.
Each `fly` method does something different.
```rust
trait Pilot {
fn fly(&self);
}
trait Wizard {
fn fly(&self);
}
struct Human;
impl Pilot for Human {
fn fly(&self) {
println!("This is your captain speaking.");
}
}
impl Wizard for Human {
fn fly(&self) {
println!("Up!");
}
}
impl Human {
fn fly(&self) {
println!("*waving arms furiously*");
}
}
```
When we call `fly` on an instance of `Human`, the compiler defaults to calling the method that is directly implemented on the type.
```rust
fn main() {
let person = Human;
person.fly();
}
```
The output of this code will print `*waving arms furiously*`, showing that Rust called the `fly` method implemented on `Human` directly.
To call the `fly` methods from either the `Pilot` trait or the `Wizard` trait, we need to use more specific syntax to specify which `fly` method we mean.
Here is a demonstration of this syntax,
```rust
fn main() {
let person = Human;
Pilot::fly(&person);
Wizard::fly(&person);
person.fly();
}
```
Specifying the trait name before the method name clarifies to Rust and Us which implementation of `fly` we want to call.
We could also write `Human::fly(&person)` but that us the same as `person.fly()`.
This is also a bit longer to write if we don't need to disambiguate.
Output
```
$ cargo run
Compiling traits-example v0.1.0 (file:///projects/traits-example)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.46s
Running `target/debug/traits-example`
This is your captain speaking.
Up!
*waving arms furiously*
```
Because `fly` takes a `self` parameter, if we had two *types* that both implement one *trait*, Rust would be able to figure out which implementation of a `trait` to use based on the type of `self`.
Associated function that are not methods do not have a `self` parameter.
When there are multiple types of traits that define non-method functions with the same function name, Rust doesn't always know which type you mean unless you use *fully qualified syntax*.
For example, we here we create a trait for an animal shelter that wants to name all baby dogs *Spot*.
We make an `Animal` trait with an associated non-method function `baby_name`.
The `Animal` trait is implemented for the struct `Dog`, which we also provide an associated non-method function `baby_name` directly.
```rust
trait Animal {
fn baby_name() -> String;
}
struct Dog;
impl Dog {
fn baby_name() -> String {
String::from("Spot")
}
}
impl Animal for Dog {
fn baby_name() -> String {
String::from("puppy")
}
}
fn main() {
println!("A baby dog is called a {}", Dog::baby_name());
}
```
We implement the code for naming all puppies `Spot` in the `baby_name` associated function that is defined on `Dog`.
The `Dog` type also implements the trait `Animal`, which describes characteristics that all animals have.
Baby dogs are called puppies, and that is expressed in the implementation of the `Animal` trait on `Dog` in the `baby_name` function associated with the `Animal` trait.
In `main` we call the `Dog::baby_name` function, which calls the associated function defined on `Dog` directly.
This outputs
```
$ cargo run
Compiling traits-example v0.1.0 (file:///projects/traits-example)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.54s
Running `target/debug/traits-example`
A baby dog is called a Spot
```
This isn't thew output we wanted.
We want to call the `baby_name` function that is part of the `Animal` trait that we implemented on `Dog` so that it prints `A baby dog is called a puppy`.
The technique of specifying the trait name that we used here doesn't help here.
If we changed `main` to the code below, we get a compiler error.
```rust
fn main() {
println!("A baby dog is called a {}", Animal::baby_name());
}
```
Because `Animal::baby_name` doesn't have a `self` parameter and there could be other types implements the `Animal` trait, Rust can't figure out which implementation of `Animal::baby_name` we want.
We get this compiler error
```
$ cargo run
Compiling traits-example v0.1.0 (file:///projects/traits-example)
error[E0790]: cannot call associated function on trait without specifying the corresponding `impl` type
--> src/main.rs:20:43
|
2 | fn baby_name() -> String;
| ------------------------- `Animal::baby_name` defined here
...
20 | println!("A baby dog is called a {}", Animal::baby_name());
| ^^^^^^^^^^^^^^^^^^^ cannot call associated function of trait
|
help: use the fully-qualified path to the only available implementation
|
20 | println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
| +++++++ +
For more information about this error, try `rustc --explain E0790`.
error: could not compile `traits-example` (bin "traits-example") due to 1 previous error
```
To disambiguate and tell Rust that we want to use the implementation of `Animal` for `Dog` as opposed to the implementation of `Animal` for some other type.
We need to use fully qualified syntax.
Here demonstrates how to use fully qualified syntax
```rust
fn main() {
println!("A baby dog is called a {}", <Dog as Animal>::baby_name());
}
```
Here we provide Rust with a type annotation within the angle brackets.
This indicates we want to call the `baby_name` method from the `Animal` trait as implementation on `Dog` by saying that we want to treat the `Dog` type as an `Animal` for this function call.
Here is the new output
```
$ cargo run
Compiling traits-example v0.1.0 (file:///projects/traits-example)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.48s
Running `target/debug/traits-example`
A baby dog is called a puppy
```
Here us a fully qualified syntax is defined as follows
```
<Type as Trait>::function(receiver_if_method, next_arg, ...);
```
For associated function that aren't methods, there would not be a `receiver`: there would only be the list of other arguments.
You could use fully qualified syntax everywhere that you call functions or methods.
You are allowed to omit any part of this syntax that Rust can figure out from other information in the program.
You only need to use this this more verbose syntax in cases where there are multiple implementations that use the same name and Rust needs help to identify which implementation you want to call.
## Using Supertraits to Require One Trait's Functionality Within Another Trait
Sometimes you might write a trait definition that depends on another trait.
For a type to implement the first trait, you want to require that type to also implement the second trait.
You would do this so that your trait definition can make use of the associated items of the second trait.
The trait your trait definition is relying on is called a *supertrait* of your trait.
Lets say we want to make an `OutlinePrint` trait with an `outline_print` method that will print a given value formatted so that it is framed in asterisks.
Given that a `Point` struct that implements the std library trait `Display` to result in `(x, y)`.
When we call `outline_print` on a `Point` instance that has `1` for `x` and `3` for `y`, it should print the following
```
**********
* *
* (1, 3) *
* *
**********
```
The implementation of the `outline_print` method we want to use the `Display` trait's functionality.
Therefore we need to specify that the `OutlinePrint` trait will work only for types that also implement `Display` and provide the functionality that `OutlinePrint` needs.
We can do this in the trait definition by specifying `OutlinePrint: Display`.
This technique is similar to adding a trait bound to the trait.
Here shows an implementation of the `OutlinePrint` trait
```rust
use std::fmt;
trait OutlinePrint: fmt::Display {
fn outline_print(&self) {
let output = self.to_string();
let len = output.len();
println!("{}", "*".repeat(len + 4));
println!("*{}*", " ".repeat(len + 2));
println!("* {output} *");
println!("*{}*", " ".repeat(len + 2));
println!("{}", "*".repeat(len + 4));
}
}
```
Because we specified that `OutlinePrint` requires the `Display` trait.
We can use the `to_string` function that is automatically implemented for any type that implements `Display`.
If we attempted to use `to_string` without adding a color and specifying the `Display` trait after the trait name, we would get an error saying that no method named `to_string` was found for the type `&Self` in the current scope.
Lets see what happens when we try to implement `OutlinePrint` on a type that doesn't implement `Display`, such as the `Point` struct
```rust
struct Point {
x: i32,
y: i32,
}
impl OutlinePrint for Point {}
```
We still get an error saying that `Display` is required but not implemented
```
$ cargo run
Compiling traits-example v0.1.0 (file:///projects/traits-example)
error[E0277]: `Point` doesn't implement `std::fmt::Display`
--> src/main.rs:20:23
|
20 | impl OutlinePrint for Point {}
| ^^^^^ `Point` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `Point`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
note: required by a bound in `OutlinePrint`
--> src/main.rs:3:21
|
3 | trait OutlinePrint: fmt::Display {
| ^^^^^^^^^^^^ required by this bound in `OutlinePrint`
error[E0277]: `Point` doesn't implement `std::fmt::Display`
--> src/main.rs:24:7
|
24 | p.outline_print();
| ^^^^^^^^^^^^^ `Point` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `Point`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
note: required by a bound in `OutlinePrint::outline_print`
--> src/main.rs:3:21
|
3 | trait OutlinePrint: fmt::Display {
| ^^^^^^^^^^^^ required by this bound in `OutlinePrint::outline_print`
4 | fn outline_print(&self) {
| ------------- required by a bound in this associated function
For more information about this error, try `rustc --explain E0277`.
error: could not compile `traits-example` (bin "traits-example") due to 2 previous errors
```
In order to fix this, we implement `Display` on `Point` and satisfy the constraint that `OutlinePrint` requires.
Like this
```rust
use std::fmt;
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
```
Now implementing the `OutlinePrint` trait on `Point` will compile successfully.
We can call `outline_print` on a `Point` instance to display it within an outline of asterisks.
## Using the Newtype Pattern to Implement Traits on External Types
Previously we mentioned the orphan rule that states we are only allowed to implement a trait on a type if either the trait or the type are local to our crate.
It is possible to get around this restriction using the *newtype pattern*.
This involves creating a new type in a tuple struct.
The tuple struct will have one field and be a thin wrapper around the type we want to implement a trait for.
Then the wrapper type is local to our crate, and we can implement the trait on the wrapper.
*Newtype* is a term that originates from the Haskell programming language.
There is no runtime performance penalty for using this pattern, and wrapper type is elided at compile time.
For example let's say we want to implement `Display` on `Vec<T>`, which the orphan rule prevents us from doing directly because the `Display` trait and the `Vec<T>` type are defined outside our crate.
We can make a `Wrapper` struct that holds an instance of `Vec<T>`.
Next we can implement `Display` on `Wrapper` and use the `Vec<T>` value.
```rust
use std::fmt;
struct Wrapper(Vec<String>);
impl fmt::Display for Wrapper {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "[{}]", self.0.join(", "))
}
}
fn main() {
let w = Wrapper(vec![String::from("hello"), String::from("world")]);
println!("w = {w}");
}
```
The implementation of `Display` uses `self.0` to access the inner `Vec<T>`.
Because `Wrapper` is a tuple is a tuple struct and `Vec<T>` is the item at index 0 in the tuple.
Then we can use the functionality of the `Display` trait on `Wrapper`.
The downside of this technique is that `Wrapper` is a new type, so it doesn't have the methods of the value it is holding.
We would need to implement all the methods of `Vec<T>` directly on `Wrapper` such that the methods delegate to `self.0`, which would allows us to treat `Wrapper` exactly like a `Vec<T>`.
If we wanted the new type to have every method the inner type has, implementing the `Deref` trait on the `Wrapper` to return the inner type would be a solution.
If we didn't want the `Wrapper` type to have all the methods of the inner type.
For example, to restrict the `Wrapper` type's behavior we would have to implement just the methods we do want manually.
This newtype pattern is useful even when traits are not involved.

333
Advanced Types.md Normal file
View File

@ -0,0 +1,333 @@
# Advanced Types
The Rust type system has some features that we have mentioned so far but haven't gone into detail.
To start we will go into the newtypes in general as we examine why newtypes are useful as types.
Then we will go onto type aliases, a feature similar to newtypes but slightly different semantics.
As well we will discuss the `!` and dynamically sized types.
## Using the Newtype Pattern for Type Safety and Abstraction
The newtype pattern are also useful for tasks beyond those discussed already.
This includes statically enforcing that values are never confused and indicating the units of a value.
Before we saw an example of using newtypes to indicate units: recall that the `Millimeters` and `Meters` structs wrapped `u32` values in a newtype.
If we wrote a function with a parameter of type `Millimeters`, we couldn't compile a program that accidentally tired to call function with a value of type `Meters` or a plain `u32`.
We can also use the newtype pattern to abstract away some implementation details of a type.
The new type can expose a public API that is different form the API of the private inner type.
Newtypes can also hide internal implementation.
Lets say we could provide a `People` type to wrap a `HashMap<i32, String>` that store a person's ID associated with their name.
Code using `People` would only interact with the public API we provide.
Like a method to add a name string to the `People` collect: this code wouldn't need to know that we assign an `i32` ID to names internally.
The newtype pattern is a lightweight way to achieve encapsulation to hide implementation details, which we discussed before in [Ch18](./Characteristics%20of%20OO%20Languages.md#encapsulation-that-hides-implementation-details).
## Creating Type Synonyms with Type Aliases
Rust provides the ability to declare a *type alias* to give an existing type another name.
We need to use the `type` keyword to do this.
For example we can create the alias `Kilometers` to `i32` like this.
```rust
type Kilometers = i32;
```
The alias `Kilometers` is a *synonym* for `i32`.
Unlike the `Millimeters` and `Meters` types we created before.
`Kilometers` is not a separate, new type.
Values that have the type `Kilometers` will be treated the same as values of type `i32`.
```rust
type Kilometers = i32;
let x: i32 = 5;
let y: Kilometers = 5;
println!("x + y = {}", x + y);
```
Because `Kilometers` and `i32` are the same type, we can add values of both types and we can pass `Kilometers` values to functions that take `i32` parameters.
However using this method, we don't get the type checking benefits that we get from the newtype pattern discussed earlier.
In other words, if we mix up `Kilometers` and `i32` values somewhere, the compiler will not give us an error.
The main use for type synonyms is to reduce repetition.
As an example, we might have a lengthy type like this.
```rust
Box<dyn Fn() + Send + 'static>
```
Writing this lengthy type function signatures and as type annotations all over the code can be tiresome and error prone.
Just image a project full of code like this.
```rust
let f: Box<dyn Fn() + Send + 'static> = Box::new(|| println!("hi"));
fn takes_long_type(f: Box<dyn Fn() + Send + 'static>) {
// --snip--
}
fn returns_long_type() -> Box<dyn Fn() + Send + 'static> {
// --snip--
}
```
A type alias makes this code more manageable by reducing the amount of repetition.
Here we have introduced an alias named `Thunk` for the verbose type and can replace all uses of the type with the shorter alias `Thunk`.
```rust
type Thunk = Box<dyn Fn() + Send + 'static>;
let f: Thunk = Box::new(|| println!("hi"));
fn takes_long_type(f: Thunk) {
// --snip--
}
fn returns_long_type() -> Thunk {
// --snip--
}
```
This is much easier to read and write.
Choosing a meaningful name for a type alias can help communicate your intent as well.
*Thunk* is a word for code to be evaluated at a later time, this is an appropriate name for a closure that gets stored.
Type aliases are also commonly used with the `Result<T, E>` type for repetition.
Consider the `std::io` module in the std library.
I/O operations often return a `Result<T, E>` to handle situations when operations fail to work.
This library has a `std::io::Error` struct that represents all possible I/O errors.
Many of the functions in `std::io` will be returning `Result<T, E>` where the `E` is `std::io::Error`, such as these functions in `Write` trait:
```rust
use std::fmt;
use std::io::Error;
pub trait Write {
fn write(&mut self, buf: &[u8]) -> Result<usize, Error>;
fn flush(&mut self) -> Result<(), Error>;
fn write_all(&mut self, buf: &[u8]) -> Result<(), Error>;
fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<(), Error>;
}
```
The `Result<..., Error>` is repeated a lot.
Therefore `std::io` has this type alias declaration
```rust
type Result<T> = std::result::Result<T, std::io::Error>;
```
Due to this declaration is in the `std::io` module, we can use the fully qualified alias `std::io::Result<T>`.
That is a `Result<T, E>` with the `E` filled in as `std::io::Error`.
The `Write` trait function signatures end up looking like this.
```rust
pub trait Write {
fn write(&mut self, buf: &[u8]) -> Result<usize>;
fn flush(&mut self) -> Result<()>;
fn write_all(&mut self, buf: &[u8]) -> Result<()>;
fn write_fmt(&mut self, fmt: fmt::Arguments) -> Result<()>;
}
```
This type alias helps in two ways:
- It makes code easier to write.
- *And*
- It gives us a consistent interface across all of `std::io`
Due to it being an alias, it is just another `Result<T, E>`, this means we can use any methods that work on `Result<T, E>` with it, as well as special syntax like the `?` operator.
## The Never Type that Never Returns
Rust has a special type named `!` that is known in type theory lingo as the *empty type* because it has no values.
We prefer to call it the *never type* because it stands in the place of the return type when a function will never return.
Here is an example in use.
```rust
fn bar() -> ! {
// --snip--
}
```
This code should be read as "the function `bar` returns never."
Functions that return never are called *diverging functions*.
We can't create values of the type `!` so `bar` can never possibly return.
What is the use of a type you can never create values for?
Recall the code from Ch2, part of the number guessing game.
Here is a sample of that code
```rust
let guess: u32 = match guess.trim().parse() {
Ok(num) => num,
Err(_) => continue,
};
```
Before we skipped over some details about this code.
In ch6 we discussed that `match` arms must all return the same type.
For example this code will not compile.
```rust
let guess = match guess.trim().parse() {
Ok(_) => 5,
Err(_) => "hello",
};
```
The type of `guess` in this code would have to be an integer *and* a string, and Rust requires that `guess` have only one type.
So what does `continue` return?
How are we allowed to return a `u32` from one arm and have another arm that ends with `continue`?
`continue` has a `!` value.
That is, when Rust computes the type of `guess`, it looks at both match arms, the former with a value of `u32` and the latter with a `!` value.
Because `!` can never have a value, Rust decides that the type of `guess` is `u32`.
The formal way to describe this behavior is that expressions of type `!` can be coerced into any other type.
We are allowed to end this `match` arm with `continue` because `continue` doesn't return a value.
Instead it moves control back to the top of the loop, so in the `Err` case, we never assign a value to `guess`.
The never type is useful with the `panic!` macro as well.
Remember the `unwrap` function that we call on `Option<T>` values to produce a value or panic with this definition:
```rust
impl<T> Option<T> {
pub fn unwrap(self) -> T {
match self {
Some(val) => val,
None => panic!("called `Option::unwrap()` on a `None` value"),
}
}
}
```
Here, the same thing happens as in the `match` case form before.
Rust sees that `val` has the type `T` and `panic!` has the type `!`, so the result of the overall `match` expression is `T`.
This works because `panic!` doesn't produce a value, it ends the program.
In the `None` case, we will not be returning a value form `unwarp` so this code is valid.
One final expression that has the type `!` is a `loop`.
```rust
print!("forever ");
loop {
print!("and ever ");
}
```
This loop never ends, so `!` is the value of the expression.
However, this wouldn't be true if we included a `break`, because the loop would terminate when it got to the `break`.
## Dynamically Sized Types and the `Sized` Trait
Rust must know certain details about its types, such as how much space to allocate for a value of a particular type.
This leaves one corner of its type system a little confusing at first: the concept of *dynamically sized types*.
Sometimes referred to as *DSTs* or *unsized types*, these types let us write code using values whose size we can know only at runtime.
Lets look into the details of a dynamically sized type called `str`, which we have been using throughout.
This does not include `&str`, but `str` on its own, is a DST.
We can't know how long the string is until runtime, meaning we can't create a variable of type `str`, nor can we make that argument of type `str`.
Consider this code, which will not compile.
```rust
let s1: str = "Hello there!";
let s2: str = "How's it going?";
```
Rust needs to know how much memory to allocate for any value of a particular type, and all values of a type must use the same amount of memory.
If Rust allowed use to write this code, these two `str` values would need to take up the same amount of memory.
These two have different lengths:
- `s1` needs 12 bytes of storage.
- `s2` needs 15.
This is why it is not possible to create a variable holding a dynamically sized type.
So what should we do?
We should make the types of `s1` and `s2` a `&str` rather than a `str`.
Recall from the "String Slice" section from Ch4, that the slice data structure just stores the starting position and the length of the slice.
Even though a `&T` is a single value that stores the memory address of where the `T` is located, a `&str` is *two* values.
The address of the `str` and its length.
We can know the size of a `&str` value at compile time: it's twice the length of a `usize`.
This means we always know the size of a `&str`, no matter how long the string it refers to is.
Generally this is the way in which dynamically sized types are used in Rust, they have an extra but of metadata that stores the size of the dynamic information.
The golden rule of dynamically sized types is that we must always put values of dynamically sized types behind a pointer of some kind.
We can combine `str` with all kinds of pointers.
For example `Box<str>` or `Rc<str>`.
In fact we have seen this before but with a different dynamically sized type: traits.
Every trait is a dynamically sized type we can refer to by using the name of the trait.
In Ch18 in ["Using Trait Objects That Allow for Values of Different Types"](./Characteristics%20of%20OO%20Languages.md#encapsulation-that-hides-implementation-details), we mentioned that to use trait as trait objects, we must put them behind a pointer, such as `&dyn Trait` or `Box<dyn Trait>` (`Rc<dyn Trait>` would work as well).
To work with DSTs, Rust provides the `Sized` trait to determine whether or not a type's size is known at compile time.
This trait is automatically implemented for everything whose size is known at compile time.
Additionally Rust implicitly adds a bound on `Sized` to every generic function.
That is, a generic function definition like this:
```rust
fn generic<T>(t: T) {
// --snip--
}
```
This is actually treated as though we had written this:
```rust
fn generic<T: Sized>(t: T) {
// --snip--
}
```
By default, generic functions will work only on types that have a known size at compile time.
However, you can use the following special syntax to relax this restriction.
```rust
fn generic<T: ?Sized>(t: &T) {
// --snip--
}
```
A trait bound on `?Sized` means "`T` may or may not be `Sized`".
This notation overrides the default that generic types must have a known size at compile time.
The `?Trait` syntax with this meaning is only available for `Sized`, not any other traits.
Note that we switched the type of the `t` parameter from `T` to `&T`.
Because the type might not be `Sized`, we need to use it behind some kind of pointer.
Here we have chosen to use a reference.

View File

@ -346,7 +346,7 @@ Then we call the `unwrap` method, we know this will never panic.
We know the methods on `Post` ensure that `state` will always contain a `Some` value when those methods are done.
This is a case where we have more information than the compiler (previously discussed in [Ch 9]()) when we know that a `None` value is never possible, even though the compiler isn't able to understand that.
This is a case where we have more information than the compiler (previously discussed in [Ch 9](./Error%20Handling.md#cases-in-which-you-have-more-info-than-the-compiler)) when we know that a `None` value is never possible, even though the compiler isn't able to understand that.
Now at this point, when we call `content` on the `&Box<dyn State>`, deref coercion will take effect on the `&` and the `Box` so the `content` method will ultimately be called on the type that implements the `State` trait.

502
Macros.md Normal file
View File

@ -0,0 +1,502 @@
# Macros
The term *macro* refers to a family of features in Rust: *declarative* macros with `macro_rules!` and three kinds of *procedural* macros:
- Custom `#[derive]` macros that specify code added with the `derive` attribute used on structs and enums.
- Attribute-like macros that define custom attributes usable on any item
- Function-like macros that look like function calls but operate on the tokens specified as their argument
Each will be discussed, but let's first look at why we even need macros when we already have functions.
## The Difference Between Macros and Functions
Fundamentally macros are a way of writing code that writes other code, which is known as *metaprogramming*.
In Appendix C, we discuss the `derive` attribute, which generates an implementation of various traits for you.
All of these macros *expand* to produce more code than the code you have written manually.
Metaprogramming is useful for reducing the amount of code you have to write and maintain, which is also one of the roles of functions.
Macros have some additional powers that functions don't have.
A function signature must declare the number and type of parameters the function has.
Macros can take a variable number of parameters.
To show this: we can call `println!("hello")` with one argument or `println!("hello {}", name)` with two arguments.
Macros are also expanded before the compiler interprets the meaning of the code, so a macro can.
For example, implement a trait on a give type.
A function cannot, because it gets called at runtime and a trait needs to be implemented at compile time.
The downside to implementing a macro instead of a function is that macro definition are more complex than function definitions because you are writing Rust code that writes Rust code.
This indirection of macro definitions are generally more difficult to read, understand and maintain than function definitions.
Another important difference between macros and functions is that you must define macros or bring them into scope *before* you call them in a file.
This is opposed to functions where you can define anywhere and call anywhere.
## Declarative Macros with `macro_rules!` for General Metaprogramming
The most widely used form of macros in Rust is the *declarative macro*.
These are sometimes also referred to as "macros by example," *"`macro rules` macros"* or just plain "macros."
At their core, declarative macros allow you to write something similar to a Rust `match` expression.
`match` expressions are control structures that take an expression, compare the resulting value of the expression to patterns, and then run the code associated with the matching pattern.
Macros also compare a value to patterns that are associated with particular code.
In this situation, the value is the literal Rust source code passed to the macro, the patterns are compared with the structure of that source code and the code associated with each pattern, when matched, replaces the code passed to the macro.
This happens during compilation.
In order to define a macro, you use the `macro_rules!` construct.
Now lets explore how to use `macro_rules!` by looking at how the `vec!` macro is defined.
Ch8 covered how we can use the `vec!` macro to create a new vector with particular values.
For example, the following macro creates a new vector containing three integers:
```rust
let v: Vec<u32> = vec![1, 2, 3];
```
We could also use the `vec!` macro to make a vector of two integers or a vector of five string slices.
We wouldn't be able to use a function to do the same because we wouldn't know the number or type values up front.
Here shows a slightly simplified definition of the `vec!` macro.
```rust
#[macro_export]
macro_rules! vec {
( $( $x:expr ),* ) => {
{
let mut temp_vec = Vec::new();
$(
temp_vec.push($x);
)*
temp_vec
}
};
}
```
Note: The actual definition of the `vec!` macro in std library includes code to preallocate the correct amount of memory up front.
That code is an optimization that we don't include here to make the example simpler.
The `#[macro_export]` annotation indicates that this macro should be made available whenever the crate in which the macro is defined is brought into scope.
We then start the macro definition with `macro_rules!` and the name of the macro we are defining *without* the exclamation mark.
Then name here `vec` is followed by curly brackets denoting the body of the macro definition.
The structure in the `vec!` body is similar to the structure of a `match` expression.
Here we have one arm with the pattern `( $( $x:expr ),* )`, followed by `=>` and the block of code associated with this pattern.
If the pattern matches, the associated block of code will be emitted.
Given this is the only pattern in this macro, there is only one valid way to match; any other pattern will result in an error.
More complex macros will have more than one arm.
Valid pattern syntax in macro definitions is different than the pattern syntax covered in Ch19.
This is because macro patterns are matched against Rust code structure rather than values.
Now lets go over what the pattern pieces in the previous examples mean.
The full macro pattern syntax can be seen in the [Rust Reference](https://doc.rust-lang.org/reference/macros-by-example.html).
First, we use a set of parentheses to encompass the whole pattern.
We use a dollar sign (`$`) to declare a variable in the macro system that will contain the Rust code matching the pattern.
The dollar sign makes it clear this a macro variable as opposed to a regular Rust variable.
Next comes a set of parentheses that captures values that match the pattern within the parentheses for use in the replacement code.
Within `$()` is `$x: expr`, this matches any Rust expression and gives the expression the name `$x`.
The comma following `$()` indicates that a literal comma separator character must appear between each instance of the code that matches the code within `$()`.
The `*` specifies that the pattern matches zero or more of whatever precedes the `*`.
When we call this macro with `vec![1, 2, 3];`, the `$x` pattern matches three times with the three expressions `1`, `2`, and `3`.
Now lets look at the pattern in the body of the code associated with this arm: `temp_vec.push()` within `$()*` is generated for each part that matches `$()` in the pattern zero or more times depending on how many times the pattern matches.
The `$x` is replaced with each expression matched.
When we call this macro with `vec![1, 2, 3];` the code generated that replaces this macro call will be this:
```rust
{
let mut temp_vec = Vec::new();
temp_vec.push(1);
temp_vec.push(2);
temp_vec.push(3);
temp_vec
}
```
Here we dined a macro that can take any number of arguments of any type and can generate code to create a vector containing the specified elements.
To learn more about how to write macros, read online documentation or other resources like ["The Little Book of Rust Macros"](https://veykril.github.io/tlborm/) started by Daniel Keep and continued by Lukas Wirth.
## Procedural Macros for Generating Code from Attributes
The second form of macros is the *procedural macro*, which acts more like a function (and is a type of procedure).
Procedural macros accept some code as an input, operate on this code, and produce some code as an output rather than matching against patterns and replacing the code with other code as declarative macros do.
The three kinds of procedural macros are custom derive, attribute-like and function-like and all work in a similar way.
When creating procedural macros, the definitions must reside in their own crate with a special enum crate type.
This is for complex technical reasons that the Rust team hopes to eliminate in the future.
Here we show how to define a procedural macro, where `some_attribute` is placeholder for using a specific macro variety.
```rust
use proc_macro;
#[some_attribute]
pub fn some_name(input: TokenStream) -> TokenStream {
}
```
This function that defines a procedural macro takes `TokenStream` as an input and produces a `TokenStream` as an output.
The `ToeknnStream` type is defined by the `proc_macro` crate that is included with Rust and represents a sequence of tokens.
This is the core of the macro: the source code that the macro is operating on makes up the input `TokenStream` and the code the macro produces is the output `TokenStream`.
This function also has an attribute attached to it which specifies what kind of procedural macro we are creating.
We can have multiple kinds of procedural macros in the same crate.
Now we will look at the different kinds of procedural macros.
First we will start with a custom derive macro and then go on to explain the small dissimilarities that make the other forms different.
## How to Write a Custom `derive` Macro
Lets start with a crate named `hello_macro` which defines a trait named `HelloMacro` with one associated function named `hello_macro`.
Rather than forcing our users to implement the `HelloMacro` trait for each of their types, we will provide a procedural macro so users can annotate their type with `#[derive(HelloMacro)]` to get a default implementation of the `hello_macro` function.
The default implementation will print `Hello, Macro! My name is TypeName!` where `TypeName` is the name of the type which this trait has been defined.
We will write a crate that enables another programmer to write code like this below using our crate.
```rust
use hello_macro::HelloMacro;
use hello_macro_derive::HelloMacro;
#[derive(HelloMacro)]
struct Pancakes;
fn main() {
Pancakes::hello_macro();
}
```
This will print `Hello, Macro! My name is Pancakes!` when we are done.
The first step will be to make a new library crate using this:
```
$ cargo new hello_macro --lib
```
Then we will define the `HelloMacro` trait and its associated function.
```rust
pub trait HelloMacro {
fn hello_macro();
}
```
*src/lib.rs*
We have a trait and its function.
Now at this point our crate user could implement the trait to achieve the desired functionality, like this:
```rust
use hello_macro::HelloMacro;
struct Pancakes;
impl HelloMacro for Pancakes {
fn hello_macro() {
println!("Hello, Macro! My name is Pancakes!");
}
}
fn main() {
Pancakes::hello_macro();
}
```
However they would need to write the implementation block for each type they wanted to use with `hello_macro`.
We want to spare them from having to do this.
Additionally, we can't yet provide the `hello_macro` function with default implementation that will print the name of the type the trait is implemented on.
Rust doesn't have reflection capabilities, so it cannot look up the type's name at runtime.
Instead we need a macro to generate code at compile time.
Next is to define the procedural macro.
At the time of writing this, procedural macros need to be in their own crate.
The convention for structuring crates and macro crates is as follows.
For a crate named `foo`, a custom derive procedural macro crate is called `foo_derive`.
Now lets start a new crate called `hello_macro_derive` inside our `hello_macro` project.
```
$ cargo new hello_macro_derive --lib
```
The two crates are tightly related, so we create the procedural macro crate within the directory of our `hello_macro` crate.
If we change the trait definition in `hello_macro`, we will have to change the implementation of the procedural macro in `hello_macro_derive` as well.
The two crates will need to be published separately, and programmers using these crates will need to add both as dependencies and bring them both into scope.
We instead could have the `hello_macro` crate use `hello_macro_derive` as a dependency and re-export the procedural macro code.
The way we have structured the project makes it possible for programmers to use `hello_macro` even if we don't want the `derive` functionality.
We need to declare the `hello_macro_derive` crate as a procedural macro crate.
We also need functionality form the `syn` and `quote` crates.
So we need to add them as dependencies.
Add this to the *Cargo.toml* file for the `hello_macro_derive`.
```toml
[lib]
proc-macro = true
[dependencies]
syn = "2.0"
quote = "1.0"
```
To start defining the procedural macro, place the code into your *src/lib.rs* file for the `hello_macro_derive` crate.
Note that this code will not compile unless we add a definition for the `impl_hello_macro` function.
```rust
use proc_macro::TokenStream;
use quote::quote;
#[proc_macro_derive(HelloMacro)]
pub fn hello_macro_derive(input: TokenStream) -> TokenStream {
// Construct a representation of Rust code as a syntax tree
// that we can manipulate
let ast = syn::parse(input).unwrap();
// Build the trait implementation
impl_hello_macro(&ast)
}
```
*hello_macro_derive/src/lib.rs*
Note that we have split the code into the `hello_macro_derive` function, which is responsible for parsing the `TokenStream` and the `impl_hello_macro` function, which is responsible for transforming the syntax tree.
This makes writing a procedural macro more convenient.
The code in the outer function (`hello_macro_dervie` in this case) will be the same for almost every procedural macro crate you see or create.
The code specified in the body of the inner function (`impl_hello_macro` in this case) will be different depending on your procedural macro's purpose.
We have introduced three new crates: `proc_macro`, [`syn`](https://crates.io/crates/syn), and [`quote`](https://crates.io/crates/quote).
The `proc_macro` crate comes with Rust, so we don't need to add that to the dependencies in *Cargo.toml*.
The `proc_macro` crate is the compiler's API that allows us to read and manipulate Rust code from our code.
The `syn` crate parses Rust code form a string into a data structure that we can perform operations on.
The `quote` crate turns `syn` data structures back into Rust code.
These crate makes it much simpler to parse any sort of Rust code we might want to handle: writing a full parser for Rust code is no simple task.
The `hello_macro_derive` function will be called when a user of our library specifies `#[derive(HelloMacro)]` on a type.
This is possible because we have annotated the `hello_macro_derive` function here with `proc_macro_derive` and specified the name `HelloMaro`.
This matches our trait name, this is the convention most procedural macros follow.
The `hello_macro_derive` function first converts the `input` from a `TokenStream` to a data structure that we can then interpret and perform operations on.
This is where `syn` comes into aciton.
The `parse` function in `syn` takes a `TokenStream` and shows the relevant parts of the `DeriveInput` struct we get form parsing the `struct Pancakes;` string.
```rust
DeriveInput {
// --snip--
ident: Ident {
ident: "Pancakes",
span: #0 bytes(95..103)
},
data: Struct(
DataStruct {
struct_token: Struct,
fields: Unit,
semi_token: Some(
Semi
)
}
)
}
```
The fields of this struct show that the Rust code we have parsed is a unit struct with the `ident` (identifier, this means the name) of `Pancakes`.
There are more fields on this struct for describing all kinds of Rust code.
Check the [`syn` documentation for `DeriveInput`](https://docs.rs/syn/2.0/syn/struct.DeriveInput.html) for more info.
Soon the `impl_hello_macro` function will be defined, which is where we will build the new Rust code we want to include.
Before we do this.
Note that the output for our derive macro is also a `TokenStream`.
The returned `TokenStream` is added to the code that our crate users write, so when they compile their own crate, they will get the extra functionality that we will provide in the modified `TokenStream`.
You may have noticed that we are calling `unwrap` to cause the `hello_macro_derive` function to panic if the call to the `syn::parse` function fails.
This is necessary for our procedural macro to panic on errors because `proc_macro_derive` functions must return `TokenStream` rather than `Result` to conform to the procedural macro API.
Here we have simplified this example by using `unwrap`; in production you should provide more specific error messages about what went wrong by using `panic!` or `expect`.
Note that we have the code to turn the annotated Rust code from a `TokenStream` into a `DeriveInput` instance.
Now we will generate the code that implements the `HelloMacro` trait on the annotated type.
This is shown here.
```rust
fn impl_hello_macro(ast: &syn::DeriveInput) -> TokenStream {
let name = &ast.ident;
let gen = quote! {
impl HelloMacro for #name {
fn hello_macro() {
println!("Hello, Macro! My name is {}!", stringify!(#name));
}
}
};
gen.into()
}
```
We get an `Ident` struct instance containing the name (identifier) of the annotated type using `ast.idetn`.
The struct before shows that when we run the `impl_hello_macro` function on the code form before that.
The `ident` we get will have the `ident` field with a value of `"Pancakes"`.
The `name` variable here will contain an `Ident` struct instance that, when printed will be the string `"Pancakes"`, the name of the struct from way before.
The `quote!` macro lets us define the Rust code that we want to return.
The compiler expects something different to the direct result of the `quote!` macro's execution, so we need to convert it to a `TokenStream`.
We do this by calling the `into` method, this consumes this intermediate representation and returns a value of the required `TokenStream` type.
The `quote!` macro also provides some very interesting templating mechanics.
We can enter `#name` and `quote!` will replace it with the value in the variable `name`.
You can even do some repetition similar to the way regular macros work.
Check the [`quote` crate's docs](https://docs.rs/quote) for a thorough introduction.
We want our procedural macro to generate an implementation of our `HelloMacro` trait for the type the user annotated, which we can get by using `#name`.
The trait implementation has the one function has the one function `hello_macro`, whose body contains the functionality we want to provide.
Printing `Hello, Macro! My name is` and then the name of the annotated type.
The `stringify!` macro used here is built into Rust.
This takes a Rust expression, such as `1 + 2`, and at compile time turns the expression into a string literal, such as `"1 + 2"`.
This is different than `format!` or `println!`, macros which evaluate the expression and then turn the result into a `String`.
It is possible that the `#name` input may be an expression to print literally, so we use `stringify!`.
Using `stringify!` also saves an allocation by converting `#name` to a string literal at compile time.
Now at this point, `cargo build` should complete successfully in both `hello_macro` and `hello_macro_derive`.
Now we will hook these crates to the code from before to see the procedural macro in action.
Create a new binary project in your *projects* directory using `cargo new pancakes`.
We need to add `hello_macro` and `hello_macro_derive` as dependencies in the `pancakes` crate's *Cargo.toml*.
If you are publishing your versions of `hello_macro` and `hello_macroderive` to [crates.io](https://crates.io) they would be regular dependencies.
If not you can specify them as `path` dependencies as follows:
```toml
hello_macro = { path = "../hello_macro" }
hello_macro_derive = { path = "../hello_macro/hello_macro_derive" }
```
Now you can put the code from way before into *src/main.rs*, and run `cargo ran`.
It should print `Hello, Macro! My name is Pancakes!`.
The implementation of the `HelloMacro` trait form the procedural macro was included without the `pancakes` crate needing to implement it.
The `#[derive(HelloMacro)]` added the trait implementation.
## Attribute-like macros
These kinds of macros are similar to custom derive macros, but instead of generating code for the `derive` attribute, they allow you to create new attributes.
They are also more flexible: `derive` only works for structs and enums.
Attributes can be applied to other items as well, such functions.
Here is an example of using an attribute-like macro: say you have an attribute named `route` that annotates functions when using a web application framework.
```rust
#[route(GET, "/")]
fn index() {
```
This `#[route]` attribute would defined by the framework as a procedural macro.
The signature of the macro definition function would look like this:
```rust
#[proc_macro_attribute]
pub fn route(attr: TokenStream, item: TokenStream) -> TokenStream {
```
Here, we have two parameters of type `TokenStream`.
The first is for the contents of the attribute: the `GET, "/"` part.
The second is the body of the item the attribute is attached to: here it is `fn index() {}` and the rest of the function's body.
Other than that, attribute-like macros work the same way as custom derive macros.
You create a crate with the `proc-macro` crate type and implement a function that generates the code you want.
## Function-like macros
Function-like macros define macros that look like function calls.
This is similar to `macro_rules!` macros.
They are more flexible than functions.
For example they can take an unknown number of arguments.
However `macro_rules!` macros can be defined only using the match-like syntax that was discussed in the [ “Declarative Macros with `macro_rules!` for General Metaprogramming"](./Macros.md#declarative-macros-with-macro_rules-for-general-metaprogramming) section from earlier before.
Function-like macros take a `TokenStream` parameter and their definition manipulates that `TokenStream` using Rust code as the other two types of procedural macros do.
Here is an example of a function-like macro is an `sql!` macro that may be called like this:
```rust
let sql = sql!(SELECT * FROM posts WHERE id=1);
```
This macro would parse the SQL statement inside it and check that is syntactically correct.
This is much more complex processing than a `macro_rules!` macro can do.
The `sql!` macro would be defined like this:
```rust
#[proc_macro]
pub fn sql(input: TokenStream) -> TokenStream {
```
The definition is similar to the custom derive macro's signature: we receive the tokens that are inside the parentheses and return the code we wanted to generate.
s

View File

@ -1 +1,44 @@
# Patterns and Matching
*Patterns* are a special syntax in Rust for matching against the structure of types, both complex and simple.
Using patterns in conjunction with `match` expressions and other constructs give you more control over a program's flow.
A pattern consists of some combination of the following:
- Literals
- Destructured arrays, enums, structs, or tuples
- Variables
- Wildcards
- Placeholders
Some examples include `x`, `(a, 3)` and `Some(Color::Red)`.
In the contexts in which patterns are valid, these components describe the shape of data.
Our program then matches values against the patterns to determine whether it has the correct shape of data to continue running a particular piece of code.
In order to use a pattern, we compare it to some value.
If the pattern matches the value, we use the value parts in our code.
Recall the `match` expression that used patterns, such as the coin-sorting machine example.
If the value fits the shape of the pattern, we can use the named pieces.
If it doesn't, the code associated with the pattern won't run.
This chapter is intended to be a reference on all things related to patterns.
We will cover:
- Valid places to use patterns [Section Link Here]()
- Difference between refutable and irrefutable patterns [Section Link Here](./Refutability.md)
- Different kinds of pattern syntax [Section Link Here](./Pattern%20Syntax.md)
By the end you will know how to use patterns to express many concepts in a clear way.
## Summary
Patterns are very useful in distinguishing between different kinds of data.
When used in `match` expressions, Rust ensures your patterns cover every possible value, or your program will not compile.
Patterns in `let` statements and function parameters make those constructs more useful, enabling the destructuring of values into smaller parts at the same time as assigning to variables.
Then we can create simple or complex patterns to suit our needs.

686
Pattern Syntax.md Normal file
View File

@ -0,0 +1,686 @@
# Pattern Syntax
Here we gather all the syntax valid in patterns and discuss why and when you might want to use each one.
## Matching Literals
As you saw previously in Ch6, you can match patterns against literals directly.
Here is an example of this
```rust
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything"),
}
```
The code prints `one` because the value in `x` is 1.
This syntax is useful when you want your code to take an action if it gets a particular concrete value.
## Matching Named Variables
Named variables are irrefutable patterns that match any value, and we have used them many times.
However, there is a complication when you use named variables in `match`, `if let`, or `while let` expressions.
Because each kinds of expression starts a new scope, variables declared as part of a pattern inside the expression will shadow those with the same name outside, as is the case with all variables.
Here we declare a variable named `x` with the value `Some(5)` and a variable `y` with the value `10`.
Next we create a `match` expression on the value `x`.
Look at the patterns in the match arms and `println!` at the end, and try to figure out what the code will print before running this code or reading further.
```rust
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got 50"),
Some(y) => println!("Matched, y = {y}"),
_ => println!("Default case, x = {x:?}"),
}
println!("at the end: x = {x:?}, y = {y}");
```
Lets run through what happens when the `match` expression runs.
The pattern in the first match arm doesn't match the defined value of `x`, so the code continues.
The pattern in the second match arm introduces a new variable named `y` that will match any value inside a `Some` value.
Because we are in a new scope inside the `match` expression, this is a new `y` variable, not the `y` we declared at the beginning with the value 10.
This new `y` binding will match any value inside a `Some`, which is what we have in `x`.
Therefore the new `y` binds to the inner value of the `Some` in `x`.
That value is `5` so the expression for that arm executes and prints `Matched , y = 5`.
If `x` has been a `None` value instead of `Some(5)`, the patterns in the first two arms wouldn't have matched. so the value would have matched to the underscore.
We didn't introduce the `x` variable in the pattern of the underscore arm, so the `x` in the expression is still the outer `x` that hasn't been shadowed.
For this hypothetical case, the `maatch` would pint `Default case, x = None`.
When the `match` expression is done, its scope ends, and so does the scope of the inner `y`.
The last `println!` produces `at the end: x = Some(5), y = 10`.
To create a `match` expression that compares the values of the outer `x` and `y` rather than introducing a new variable which shadows the exiting `y` variable.
We would need to use a match guard conditional instead.
This will be covered later in the ch.
## Multiple Patterns
You can match multiple patterns using the `|` syntax, which is the pattern *or* operator.
For example in the following code we match the value of `x` against the match arms, the first of which has an *or* option, meaning if the value of `x` matches either of the values in that arm, that arm's code will run
```rust
let x = 1;
match x {
1 | 2 => println!("one or two"),
3 => println!("three"),
_ => println!("anything"),
}
```
This code prints `one or two`.
## Matching Ranges of Values with `..=`
The `..=` syntax allows us to match to an inclusive range of values.
Here when a pattern matches any of the values within the given range, that arm will execute
```rust
let x = 5;
match x {
1..=5 => println!("one through five"),
_ => println!("something else"),
}
```
If `x` is 1, 2, 3, 4, or 5, the first arm will match.
This syntax is more convenient for multiple match values that using the `|` operator to express the same idea.
If we were to use `|` we would need to do `1 | 2 | 3 | 4 | 5`.
Specifying a range is much shorter, especially if we want to match, say any number between 1 and 1,000.
The compiler checks that the range isn't empty at compile time, because only types for which Rust can tell if a range is empty or not are `char` and numeric values, ranges are only allowed with numeric or `car` values.
Rust can tell that `'c'` is within the first pattern's range and prints `early ASCII letter`.
## Destructuring to Break Apart Values
We can also use patterns to destructure structs, enums, and tuples to use different parts of these values.
We will walk through each value.
### Destructuring Structs
This code shows a `Point` struct with two fields, `x` and `y`, that we can break apart using a patter with a `let` statement
```rust
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 0, y: 7 };
let Point { x: a, y: b } = p;
assert_eq!(0, a);
assert_eq!(7, b);
}
```
This creates the variables `a` and `b` that match the values of the `x` and `y` fields of the `p` struct.
This shows that the names of the variables in the pattern don't have to match the field names of the struct.
However it is common to match the variable names to the field names to make it easier to remember which variables came from which fields.
Because of this common usage, and because writing `let Point { x: x, y: y} = p;` contains a lot of duplication, Rust has a shorthand for patterns that match struct fields.
You only need to list the name of the struct field, and the variables created from the pattern will have the same names.
This code behaves in the same way as om the code before, but the variables created in the `let` pattern are `x` and `y` instead of `a` and `b`.
```rust
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 0, y: 7 };
let Point { x, y } = p;
assert_eq!(0, x);
assert_eq!(7, y);
}
```
This code creates the variables `x` and `y` that match the `x` and `y` fields of the `p` variable.
The outcome is that the variables `x` and `y` contain the values form the `p` struct.
We can also destructure with literal values as part of the struct pattern rather than creating variables for all the fields.
Doing this allows us to test some of the fields for particular values while creating variables to destructure the other fields.
Here we have a `match` expression that separates `Point` values into three cases.
Points that lie directly on the `x` axis (which is true when `y = 0`)
On the `y` axis (`x = 0`)
Or Neither
```rust
fn main() {
let p = Point { x: 0, y: 7 };
match p {
Point { x, y: 0 } => println!("On the x axis at {x}"),
Point { x: 0, y } => println!("On the y axis at {y}"),
Point { x, y } => {
println!("On neither axis: ({x}, {y})");
}
}
}
```
The first arm will match any point that lies on the `x` axis by specifying that the `y` field matches if its value matches the literal `0`.
The pattern still creates an `x` variable that we can use in the code for this arm.
The second arm matches any point on the `y` axis by specifying that the `x` field matches if its value is `0` and creates a variable `y` for the value of the `y` field.
The third arm doesn't specify any literals, so it matches any other `Point` and creates variables that we can use in the code for this arm.
Here the value `p` matches the second arm by virtue of `x` containing a 0, so this code will print `On the x axis at 0`.
### Destructuring Enums
We have destructured enums before, but haven't explicitly discussed that the pattern to destructure an enum corresponds to the way the data stored within the enum is defined.
For example, here we use the `Message` enum from Ch6 and write a `match` with patterns that will destructure each inner value.
```rust
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
fn main() {
let msg = Message::ChangeColor(0, 160, 255);
match msg {
Message::Quit => {
println!("The Quit variant has no data to destructure.");
}
Message::Move { x, y } => {
println!("Move in the x direction {x} and in the y direction {y}");
}
Message::Write(text) => {
println!("Text message: {text}");
}
Message::ChangeColor(r, g, b) => {
println!("Change the color to red {r}, green {g}, and blue {b}");
}
}
}
```
This code will print `Change the color to red 0, green 160, and blue 255`.
Try changing the value of `msg` to see the code form the other arms run.
For enum variants without any data, like `Message::Quit`.
We can't destructure the value any further.
We can only match on the literal `Message::Quit` value and no variables are in that pattern.
For struct-like enum variants, like `Message::Move`.
We can use a pattern similar to the pattern we specify to match structs.
After the variant name, we place curly brackets and then list the fields with variable so we break apart the pieces to use in the code for this arm.
We did use the shorthand form as we did before.
For tuple-like enum variants, like `Message::Write` that holds a tuple with one element and `Message::ChangeColor` that holds a tuple with three elements.
The pattern is similar to the pattern we specify to match tuples.
The number of variables in the pattern must match the number of elements in the variant we are matching.
### Destructuring Nested Structs and Enums
So we have seen examples that have all been matching structs or enums on level deep.
Matching can work on nested items too.
For example, we can refactor the code form before to support RGB and HSV colors in the `ChangeColor` message.
```rust
enum Color {
Rgb(i32, i32, i32),
Hsv(i32, i32, i32),
}
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(Color),
}
fn main() {
let msg = Message::ChangeColor(Color::Hsv(0, 160, 255));
match msg {
Message::ChangeColor(Color::Rgb(r, g, b)) => {
println!("Change color to red {r}, green {g}, and blue {b}");
}
Message::ChangeColor(Color::Hsv(h, s, v)) => {
println!("Change color to hue {h}, saturation {s}, value {v}");
}
_ => (),
}
}
```
The first arm in the `match` expression, matches a `Message::ChangeColor` enum variant that contains a `Color::Rgb` variant.
Then the pattern binds to the three inner `i32` values.
The second arm also matches a `Message::ChangeColor` enum variant, but the inner enum matches `Color::Hsv` instead.
We can specify these complex conditions in one `match` expression, even though two enums are involved.
### Destructuring Structs and Tuples
We can also mix, match and nest destructuring patterns in even more complex ways.
This example shows a complicated destructure where we nest structs and tuples inside a tuple and destructure all the primitive values out
```rust
let ((feet, inches), Point { x, y }) = ((3, 10), Point { x: 3, y: -10 });
```
Here the code lets us break complex types into their component parts so we can use the values we are interested in separately.
Destructuring with patters is a convenient way to use pieces of values such as the value from each field in a struct, separately from each other.
## Ignoring Values in a Pattern
Sometimes it is useful to ignore values in a pattern, such as in the last arm of a `match`, to get a catchall that doesn't actually do anything but does account for all remaining possible values.
There are a few ways to ignore entire values or pats of values in a pattern:
- Using the `_` pattern
- Using the `_` pattern within another pattern
- Using a name that starts with an underscore
- Using `..` to ignore remaining parts of a value.
### Ignoring an Entire Value with `_`
We have used the underscore as a wildcard pattern that will match any value but not bind to the value.
This is particularly useful as the last arm in a `match` expression.
We can also use it in any pattern, including function parameters
```rust
fn foo(_: i32, y: i32) {
println!("This code only uses the y parameter: {y}");
}
fn main() {
foo(3, 4);
}
```
This will completely ignore the value `3` that is passed as the first argument.
You will print `This code only uses the y parameter: 4`.
In most cases when you no longer need a particular function parameter, you would change the signature so it doesn't include the used parameter.
Ignoring a function parameter can be especially useful in cases when you are implementing a trait when you need a certain type signature but the function body in your implementation doesn't need one of the parameters.
Thus then you avoid getting a compiler warning about unused function parameters, as you would if you used a name instead.
### Ignoring Parts of a Value with a Nested `_`
You can also use `_` inside another pattern to ignore just part of a value.
Lets say when you want to test for only part of a value but have no use for the other parts in the corresponding code we want to run.
Here shows code responsible for managing a setting's value.
The business requirements are that the user should not be allowed to overwrite an existing customization of setting but can unset the setting and give it a value if it is currently unset.
```rust
let mut setting_value = Some(5);
let new_setting_value = Some(10);
match (setting_value, new_setting_value) {
(Some(_), Some(_)) => {
println!("Can't overwrite an existing customized value");
}
_ => {
setting_value = new_setting_value;
}
}
println!("setting is {setting_value:?}");
```
This will print `Can't overwrite an existing customized value` and then `setting is Some(5)`.
In the first arm, we don't need to match on or use the values inside either `Some` variant, but we do need to test for the case when `setting_value` and `new_setting_value` are the `Some` variant.
In this case, we print the reason for not changing `setting_value`, and it doesn't get changed.
In all other cases (if either `setting_value` or `new_setting_value` are `None`) expressed by the `_` pattern in the second arm.
We want to allow `new_setting_value` to become `setting_value`.
We could also use underscores in multiple places within one pattern to ignore particular vlaues.
Here shows an example of ignoring the second and fourth values in a tuple of five items.
```rust
let numbers = (2, 4, 8, 16, 32);
match numbers {
(first, _, third, _, fifth) => {
println!("Some numbers: {first}, {third}, {fifth}")
}
}
```
This will print `Some numbers: 2, 8, 32`, and the values 4 and 16 will be ignored.
### Ignoring an Unused Variable by Starting Its Name with `_`
If you create a variable but don't use it anywhere, Rust will usually issue a warning because an unused variable could be a bug.
However sometimes it is useful to be able to create a variable you will not use yet.
Such as when you are prototyping or just starting a project.
In this situation, you can tell Rust not to warn you about the unused variable by starting by starting the name of the variable with an underscore.
Here we create two unused variables, but when we compile, we should only get warning about one of them.
```rust
fn main() {
let _x = 5;
let y = 10;
}
```
We get a warning about not using the variable `y`, but we don't get a warning about not using `_x`.
The only difference between using only `_` and using a name that starts with an underscore.
The syntax `_x` still binds the value to the variable, whereas `_` doesn't bind at all.
To show a case where this distinction matters, this will provide us with an error.
```rust
let s = Some(String::from("Hello!"));
if let Some(_s) = s {
println!("found a string");
}
println!("{s:?}");
```
We will receive an error because the `s` value will still be moved into `_s`, which prevents us from using `s` again.
Using the underscore by itself doesn't ever bind to the value.
This code will compile without an errors because `s` doesn't get moved into `_`.
```rust
let s = Some(String::from("Hello!"));
if let Some(_) = s {
println!("found a string");
}
println!("{s:?}");
```
This code works just fine because we never bind `s` to anything; it isn't moved.
### Ignoring Remaining Parts of a Value with `..`
Values that have many parts, we can use the `..` syntax to use specific parts and ignore the rest, avoiding the need to list underscores for each ignored value.
The `..` pattern ignores any parts of a value that we haven't explicitly matched in the rest of the pattern.
In this code we have a `Point` struct that holds a coordinate in three-dimensional space.
In this `match` expression, we want to operate only on the `x` coordinate and ignore the values in the `y` and `z` fields.
```rust
struct Point {
x: i32,
y: i32,
z: i32,
}
let origin = Point { x: 0, y: 0, z: 0 };
match origin {
Point { x, .. } => println!("x is {x}"),
}
```
Here we list the `x` value and then just include the `..` pattern.
This is far quicker than having to list `y: _` and `z: _`, particularly when we are working with structs that have lots of fields in situations where only one or two fields are relevant.
The syntax `..` will expand to as many values as it needs to be.
This is an example of how to use `..` with a tuple.
```rust
fn main() {
let numbers = (2, 4, 8, 16, 32);
match numbers {
(first, .., last) => {
println!("Some numbers: {first}, {last}");
}
}
}
```
Output
```
Some numbers: 2, 32
```
The fist and last value are matched with `first` and `last`.
The `..` will match and ignore everything in the middle.
Using `..` must be unambiguous.
If it unclear which values are intended for matching and which should be ignored, Rust will give us an error.
Here shows an example of using `..` ambiguously, so it will not compile.
```rust
fn main() {
let numbers = (2, 4, 8, 16, 32);
match numbers {
(.., second, ..) => {
println!("Some numbers: {second}")
},
}
}
```
We get this compiler error
```
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
error: `..` can only be used once per tuple pattern
--> src/main.rs:5:22
|
5 | (.., second, ..) => {
| -- ^^ can only be used once per tuple pattern
| |
| previously used here
error: could not compile `patterns` (bin "patterns") due to 1 previous error
```
It is impossible for Rust to determine how many values in the tuple to ignore before matching a value with `second` and then how many further values to ignore after.
This code could mean we want to ignore `2`, bind `second` to `4` and then ignore `8` and `16` and `32`.
Or it could mean we want to ignore `2` and `4`, bind `second` to `8`, and then ignore `16` and `32`.
Or any other case.
The variable name `second` doesn't mean anything special to Rust, we get a compiler error because using `..` in two places like this ambiguous.
## Extra Conditionals with Match Guards
A *match guard* is an additional `if` condition, specified after the pattern in a `match` arm, that must also match for that arm to be chosen.
Match guards are useful for expressing complex ideas than a pattern alone allows.
They are only available in `match` expressions, not in `if let` or `while let` expressions.
The condition can use variables created in the pattern.
Here shows a `match` where the first arm has the pattern `Some(x)` and also has a match guard of `if x % 2 == 0`
This will be true if the number is even.
```rust
let num = Some(4);
match num {
Some(x) if x % 2 == 0 => println!("The number {x} is even"),
Some(x) => println!("The number {x} is odd"),
None => (),
}
```
This will print `The number 4 is even`.
When `num` is compared to the pattern in the first arm, it matches.
This is because `Some(4)` matches `Some(x)`.
Next the match guard checks whether the remainder of dividing `x` by 2 is equal to 0.
Because this is true the first arm is selected.
If `num` had been `Some(5)` instead, the match guard in the first arm would have been false.
This is because the remainder of 5 / 2 is 1, which is not equal to 0.
Rust would then go to the second arm, which doesn't have a match guard and therefore matches any `Some` variant.
There is not may to express the `if x % 2 == 0` condition within a pattern, so the match guard gives us the ability to express this logic.
The downside of this is that the compiler doesn't try to check for exhaustiveness when match guard expressions are involved.
Before we mentioned that we could use match guards to solve our pattern shadowing problem.
Recall that after we created a new variable inside the pattern in the `match` expression instead of using the variable outside the `match`.
This new variable meant we couldn't test against the value of the outer variable.
Here shows how we can use match guard to fix this problem.
```rust
fn main() {
let x = Some(5);
let y = 10;
match x {
Some(50) => println!("Got 50"),
Some(n) if n == y => println!("Matched, n = {n}"),
_ => println!("Default case, x = {x:?}"),
}
println!("at the end: x = {x:?}, y = {y}");
}
```
Output
```
Default case, x = Some(5)
at the end: x = Some(5), y = 10
```
The pattern in the second match arm doesn't introduce a new variable `y` that would shadow the outer `y`.
This means that we can use the outer `y` in the match guard.
Instead of specifying the pattern as `Some(y)`, which would have shadowed the outer `y`, we specify `Some(n)`.
This creates a new variable `n` that doesn't shadow anything because there is no `n` variable outside the `match`.
The match guard `if n == y` is not a pattern and therefore doesn't introduce new variables.
This `y` *is* the outer `y` rather than a new `y` shadowing it.
We can look for a value that has the same value as the outer `y` by comparing `n` to `y`.
You can also use the *or* operator `|` in a match guard to specify multiple patterns.
The match guard condition will apply to all the patterns.
Here shows the precedence when combining a pattern uses `|` with a match guard.
The important part of this example is that the `if y` match guard applies to `4`, `5`, and `6`, even though it might look like `if y` only applies to `6`.
```rust
let x = 4;
let y = false;
match x {
4 | 5 | 6 if y => println!("yes"),
_ => println!("no"),
}
```
This match condition states that the arm only matches if the if the value of `x` is equal to `4`, `5`, or `6` *and* if `y` is `true`.
When this runs, the pattern of the first arm matches because `x` is `4`, but the match guard `if y` is false, so the first arm is not chosen.
The code then moves on to the second arm, which does match and this program prints `no`.
The reason is that the `if` condition applies to the whole pattern `4 | 5| 6`, not only to the last value `6`.
In other words, the precedence of a match guard in relation to a pattern behaves like this
```
(4 | 5 | 6) if y => ...
```
rather than this
```
4 | 5 | (6 if y) => ...
```
After running this, the precedence behavior is evident.
If the match guard applied only to the final value in the list of values specified using the `|` operator, the arm would have matched and the program would have printed `yes`.
## `@` Bindings
The *at* operator `@` lets us create a variable that holds a value at the same time as we are testing that value for a pattern match.
Here we want to test that a `Message::Hello` `id` field is within the range `3..=7`.
We also want to bind that value to the variable `id_variable` so we can use it in the code associated with the arm.
We could name this variable `id`, the same as the field, but for this example we will use a different name.
```rust
enum Message {
Hello { id: i32 },
}
let msg = Message::Hello { id: 5 };
match msg {
Message::Hello {
id: id_variable @ 3..=7,
} => println!("Found an id in range: {id_variable}"),
Message::Hello { id: 10..=12 } => {
println!("Found an id in another range")
}
Message::Hello { id } => println!("Found some other id: {id}"),
}
```
Output
```
Found an id in range: 5
```
By specifying `id_vaariable @` before the range `3..=7`, we are capturing whatever value matched the range while also testing that the value matched the range pattern.
In the second arm, where we only have a range specified in the pattern, the code associated with the arm doesn't have a variable that contains the actual value of the `id` field.
The `id` field's values could have been 10, 11, or 12, but the code that goes with the pattern doesn't know which it is.
The pattern code isn't able to use the value form the `id` field, this is due to us not saving the `id` value in a variable.
In the last arm, where we have specified a variable without a range, we do have the value available to use in the arm's code in the variable named `id`.
The reason is that we have used the struct field shorthand syntax.
But we haven't applied any test to the value in the `id` field in this arm, as we did with the first two arms.
Any value would match this pattern.
Using `@` lets us test a value and save it in a variable within one pattern.

View File

@ -0,0 +1,254 @@
# All the Places Patterns Can Be Used
Patterns pop up in a number of places in Rust.
You use them a lot without realizing it.
## `match` Arms
As discussed in Ch6, we can use patterns in the arms of `match` expressions.
`match` expressions are defined as the keyword `match`, a value to match on, and one or more match arms that consist of a pattern and an expression to run if the value matches that arm's pattern, like this:
```
match VALUE {
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
PATTERN => EXPRESSION,
}
```
Here is an example of a `match` expression from ch6 that matches on an `Option<i32>` value in the variable `x`:
```rust
match x {
None => None,
Some(i) => Some(i + 1),
}
```
The patterns in here are the `None` and `Some(i)` on the left of each arrow.
One requirement for `match` expression is that they need to be *exhaustive* in the sense that all possibilities for the value in the `match` expression must be accounted for.
One way to ensure you have covered every possibility is to have a catchall pattern for the last arm.
For example, a variable name matching any value can never fail and thus covers every remaining case.
The particular pattern `_` will match anything, but it never binds to a variable, so it is often used in the last match arm.
The `_` pattern in more detail in the ["Ignoring Values in a Pattern"]() section.
## Conditional `if let` Expressions
In Ch6 we discussed how to use `if let` expressions mainly as a shorter way to write the equivalent of a `match` that only matches o one case.
Optionally `if let` can have a corresponding `else` containing code to run if the pattern in the `if let` doesn't match.
In this example it shows that it is also possible to mix and match `if let`, `else if` and `else if let` expressions.
This gives us more flexibility than a `match` expression in which we can express only one value to compare with the patterns.
Rust doesn't require that the conditions in a series of `if let`, `else if`, `else if let` arms relate to each other.
This code determines what color to make your background based on a series of checks for several conditions.
For example, we have created variables with hardcoded values that a real program might receive form user input.
```rust
fn main() {
let favorite_color: Option<&str> = None;
let is_tuesday = false;
let age: Result<u8, _> = "34".parse();
if let Some(color) = favorite_color {
println!("Using your favorite color, {color}, as the background");
} else if is_tuesday {
println!("Tuesday is green day!");
} else if let Ok(age) = age {
if age > 30 {
println!("Using purple as the background color");
} else {
println!("Using orange as the background color");
}
} else {
println!("Using blue as the background color");
}
}
```
Output
```
Using purple as the background color
```
If the user specifies a favorite color, that color is used as the background.
If no color is specified and today is Tuesday, the background is green.
If the user specifies their age as a string and we can parse it as a number successfully, the color is either purple or orange depending on the value of the number.
If none of the conditions apply, the background is blue.
This conditional structure lets us support complex requirements.
You can see that `if let` can also introduce new variables which shadow existing variables in the same way that `match` arms can.
The line `if let Ok(age) = age` introduces a new `age` variable that contains the `Ok` variant, shadowing the existing `age` variable.
This means we need to place the `if age > 30` condition within that block.
We cannot combine these two conditions into `if let Ok(age) = age && age > 30`.
The new `age` we want to compare to 30 isn't valid until the new scope starts with the curly bracket.
The downside of using `if let` expressions is that the compiler doesn't check for exhaustiveness, whereas with `match` expressions it does.
If we omitted the last `else` block and therefore missed handling some cases, the compiler would not alert us to the possible logic bug.
## `while let` Conditional Loops
Similar to the construction of `if let`, the `while let` conditional loop allows a `while` loop to run for as long as a pattern continues to match.
We first saw this in Ch17, where we used it to keep looping as long as a stream produced new values.
Similarly here we show a `while let` loop that waits on messages sent between threads, but in this case checking a `Result` instead of an `Option`.
```rust
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
for val in [1, 2, 3] {
tx.send(val).unwrap();
}
});
while let Ok(value) = rx.recv() {
println!("{value}");
}
```
This example will print 1, 2, and 3.
When we saw `recv` back in Ch16, we unwrapped the error directly, or interacted with it as an iterator using a `for` loop.
Here shows we can also use `while let`, because the `recv` method returns `Ok` as long as the sender is producing messages and then produces an `Err` once the sender side disconnects.
## `for` Loops
In a `for` loop, the value that directly follows the keyword `for` is a pattern.
This example demonstrates how to use a pattern in a `for` loop to destructure or break apart, a type as part of the `for` loop.
```rust
let v = vec!['a', 'b', 'c'];
for (index, value) in v.iter().enumerate() {
println!("{value} is at index {index}");
}
```
Output
```rust
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.52s
Running `target/debug/patterns`
a is at index 0
b is at index 1
c is at index 2
```
We adapt an iterator using the `enumerate` method so it produces a values and the index for that value, placed into a tuple.
The first value produced is the tuple `(0, 'a')`.
When this value is matched to the patter `(index, value)`, `index` will be `0` and `value` will be `'a'`, printing the first line out the output.
## `let` Statements
Prior to this ch, we only discussed using patterns with `match` and `if let`, but in fact, we have used patterns in other places as well.
This includes `let` statements.
For example consider this straightforward variable assignment with `let`
```rust
let x = 5;
```
Every time you used a `let` statement like this you have been using patterns, although you might not have realized it.
More formally, a `let` statement looks like this
```
let PATTERN = EXPRESSION;
```
In statements like `let x = 5;` with a variable name in the `PATTERN` slot.
The variable name is just a particularly simple form of a pattern.
Rust compares the expression against the pattern and assign any names it finds.
So in the `let x = 5;` example `x` is a pattern that means "bind what matches here to the variable `x`."
Because the name `x` is the whole pattern, this pattern effectively means "bind everything to the variable `x`, whatever the values is."
To see the pattern matching aspect of `let` more clearly, consider this, which uses a pattern with `let` to destructure a tuple.
```rust
let (x, y, z) = (1, 2, 3);
```
Here we match a tuple against a pattern.
Rust compares the value `(1, 2, 3)` to the pattern `(x, y, z)` and sees that the value matches the pattern.
Rust binds `1` to `x`, `2` to `y`, and `3` to `z`.
You can think of this tuple pattern as nesting three individual variable patterns inside it.
If the number of elements in the pattern doesn't match the number of elements in the tuple, the overall type won't match and we will get a compiler error.
Here shows an attempt to destructure a tuple with three elements into two variables, this will not work.
```rust
let (x, y) = (1, 2, 3);
```
Here is the compiler error form attempting to compile
```
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
error[E0308]: mismatched types
--> src/main.rs:2:9
|
2 | let (x, y) = (1, 2, 3);
| ^^^^^^ --------- this expression has type `({integer}, {integer}, {integer})`
| |
| expected a tuple with 3 elements, found one with 2 elements
|
= note: expected tuple `({integer}, {integer}, {integer})`
found tuple `(_, _)`
For more information about this error, try `rustc --explain E0308`.
error: could not compile `patterns` (bin "patterns") due to 1 previous error
```
In order to fix this error, we could ignore one or more of the values in the tuple using `_` or `..`
You will see this in the ["Ignoring Values in a Pattern"]()
If the problem is that we have too many variables in the pattern, the solution is to make the types match by removing variables so the number equals the number of elements in the tuple.
## Function Parameters
Function parameter can also be patterns.
The code here, which declares a function named `foo` that takes one parameter named `x` of type `i32`, should by now look familiar.
```rust
fn foo(x: i32) {
// code goes here
}
```
The `x` part is a pattern.
As we did with `let` we could match a tuple in a function's arguments to the pattern.
In this next example, this splits the values in a tuple as we pass it to a function.
```rust
fn print_coordinates(&(x, y): &(i32, i32)) {
println!("Current location: ({x}, {y})");
}
fn main() {
let point = (3, 5);
print_coordinates(&point);
}
```
This code prints `Current loaction: (3, 5)`.
The values `&(3, 5)` match the pattern `&(x, y)`, so `x` is the value `3` and `y` is the value `5`.
We can also use patterns in closure parameter lists in the same way as in function parameter lists, because closure are similar to functions (discussed in Ch 13).
Now we have seen several ways of using patterns, but patterns don't work the same in every place we can use them.
In some places, the patterns must be irrefutable; other circumstances, they can be refutable.
This will be discussed in the next section [Here](./Refutability.md)

107
Refutability.md Normal file
View File

@ -0,0 +1,107 @@
# Refutability: Whether a Pattern Might Fail to Match
Patterns come in two forms:
- Refutable
- Irrefutable
Patterns that will match for any possible value passed are *irrefutable*.
An example would be `x` in the statement `let x = 5;` because `x` matches anything and therefore cannot fail to match.
Patterns that can fail to match for some possible value are *refutable*.
An example would be `Some(x)` in the expression `if let Some(x) = a_value` because if the value in the `a_value` variable is `None` rather than `Some`, the `Some(x)` pattern will not match.
Function parameters, `let` statements and `for` loops can only accept irrefutable patterns, because the program cannot do anything meaningful when values don't match.
The `if let` and `while let` expressions and the `let`-`else` statement accept refutable and irrefutable patterns, but the compiler warns against irrefutable patterns.
This is because by definition they are intended to handle possible failure.
The functionality of a conditional is in its ability to perform differently depending on success or failure.
Generally you shouldn't have to worry about the distinction between refutable and irrefutable patterns.
However you do need to be familiar with the concept of refutability so you can respond when you see it in an error message.
In these cases, you will need to change either the pattern or the construct you are using the pattern with, depending on the intended behavior of the code.
Now lets take a look at an example of what happens when you try to use a refutable pattern where Rust requires an irrefutable pattern and vice versa.
Here shows a `let` statement, but for the pattern we have specified `Some(x)`, a refutable pattern.
As expected, this code will not compile.
```rust
let Some(x) = some_option_value;
```
If `some_option_value` was a `None` value, it would fail to match the pattern `Some(x)`, meaning the pattern is refutable.
However, the `let` statement can only accept an irrefutable pattern because there is nothing valid the code can do with a `None` value.
At compile time, Rust will complain that we have tried to use a refutable pattern where an irrefutable pattern is required
```
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
error[E0005]: refutable pattern in local binding
--> src/main.rs:3:9
|
3 | let Some(x) = some_option_value;
| ^^^^^^^ pattern `None` not covered
|
= note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
= note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
= note: the matched value is of type `Option<i32>`
help: you might want to use `let else` to handle the variant that isn't matched
|
3 | let Some(x) = some_option_value else { todo!() };
| ++++++++++++++++
For more information about this error, try `rustc --explain E0005`.
error: could not compile `patterns` (bin "patterns") due to 1 previous error
```
Because we didn't cover (and couldn't cover) every valid value with the pattern `Some(x)`, Rust rightfully produces a compiler error.
If we have a refutable pattern where an irrefutable pattern is needed, we can fix it by changing the code that uses the pattern.
Instead of using `let`, we can use `if let`.
Then if the code doesn't match, the code will just skip the code in the curly brackets, giving a way to continue validly.
Here shows a fix to the code
```rust
if let Some(x) = some_option_value {
println!("{x}");
}
```
We given the code an out, this code is perfectly valid now.
However, if we give `if let` an irrefutable pattern, such as `x`, the compiler will give a warning.
```rust
if let x = 5 {
println!("{x}");
};
```
Rust complains that it doesn't make sense to use `if let` with an irrefutable pattern
```
$ cargo run
Compiling patterns v0.1.0 (file:///projects/patterns)
warning: irrefutable `if let` pattern
--> src/main.rs:2:8
|
2 | if let x = 5 {
| ^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
= note: `#[warn(irrefutable_let_patterns)]` on by default
warning: `patterns` (bin "patterns") generated 1 warning
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.39s
Running `target/debug/patterns`
5
```
For this reason, match arms must use refutable patterns, except for the last arm, which should match any remaining values with an irrefutable pattern.
Rust allows us to use an irrefutable pattern in a `match` with only one arm.
This syntax isn't particularly useful and could be replaced with a simpler `let` statement.
Next we will cover all the syntax we can use to create patterns [here](./Pattern%20Syntax.md)

585
Unsafe Rust.md Normal file
View File

@ -0,0 +1,585 @@
# Unsafe Rust
So far the code we have discussed has had Rust's memory safety guarantees enforced at compile time.
Rust has a second language hidden inside it that doesn't enforce memory safety guarantees.
This is called *unsafety Rust*, which works just like regular Rust but it gives extra superpowers.
Unsafe Rusts exists because static analysis is conservative.
When the compiler tries to determine whether or not code upholds the guarantees, it is better for it to reject some valid programs than to accept some invalid programs.
Even if the code *may* be ok, if the compiler doesn't have enough information to be confident and therefore reject the code.
In these cases you can use unsafe code to tell the compiler, "Trust me, I know what I'm doing."
Be warned that you use unsafe Rust at your own risk.
If you use unsafe code incorrectly, problems can occur due to memory unsafety, such as null pointer dereferencing.
Rust has an unsafe alter ego is that the underlying computer hardware is inherently unsafe.
If Rust didn't allow us to do unsafe operations, you couldn't do certain tasks.
Rust needs to allow you to do low-level systems programming, such as directly interacting with the operating system or even writing your own operating system.
Working with low-level systems programming is one of the goals of the language.
## Unsafe Superpowers
To switch to unsafe Rust, you need to use the `unsafe` keyword and then start a new block that holds the unsafe code.
You can do five additional actions in unsafe Rust that you can't do in safe Rust, which we call *unsafe superpowers*.
These superpowers are:
- Dereference a raw pointer
- Call an unsafe function or method
- Access or modify a mutable static variable
- Implement an unsafe trait
- Access fields of a `union`
Note: `unsafe` does not turn off the borrow checker or disable any other of Rust's safety checks.
If you use a reference in unsafe code, it will still be checked.
The `unsafe` keyword only gives access to these five features that are then not checked by the compiler for memory safety.
You will still get some degree of safety inside of an unsafe block.
Additionally `unsafe` does not mean the code inside the block is necessarily dangerous or that it will definitely have memory safety problems.
The intent as the programmer, you will ensure the code inside an `unsafe` block will access memory in a valid way.
People are fallible, and mistakes will happen, but by requiring these five unsafe operations to be inside blocks annotated with `unsafe` you will know that any errors related to memory safety must be within an `unsafe` block.
Keep `unsafe` blocks small: you will be thankful later when you investigate memory bugs.
To isolate unsafe code as much as possible, it is best to enclose unsafe code within a safe abstraction and provide a safe API (This will be covered when we examine unsafe functions and methods).
Parts of the std library are implemented as safe abstractions over unsafe code that has been audited.
Wrapping unsafe code in a safe abstraction prevents uses of `unsafe` from leaking out into all the places that you want to use the functionality implemented with `unsafe` code, because using aa safe abstraction is safe.
Now we will look into each of the five unsafe superpowers.
We will also look at some abstractions that provide a safe interface to unsafe code.
## Deferencing a Raw Pointer
We mentioned before that the compiler ensures references are always valid.
Unsafe Rust has two new types called *raw pointers* that are similar to references
Raw pointers can be immutable or mutable, just like with references, are written as `*const T` and `*mut T`, respectively.
The asterisk isn't the dereference operator, it is part of the type name.
In the context of raw pointers, *immutable* means that the pointer can't be directly assigned to after being dereferenced.
Different from references and smart pointers, raw pointers:
- Are allowed to ignore the borrowing rules by having both immutable and mutable pointers or multiple mutable pointers to the same location
- Aren't guaranteed to point to valid memory
- Are allowed to be null
- Don't implement nay automatic cleanup
By opting out of Rust's enforced guarantees, you can give up safety in exchange for greater performance or the ability to interface with another language or hardware where Rust's guarantees don't apply.
Here is an example of how to create an immutable and a mutable raw pointer.
```rust
let mut num = 5;
let r1 = &raw const num;
let r2 = &raw mut num;
```
Note that we don't include the `unsafe` keyword here.
We can create raw pointers in safe code; we just can't dereference raw pointers outside an unsafe block (We will see this later).
We have created raw pointers by using the raw borrow operators.
`&raw const num` creates a `*const i32` immutable raw pointer.
`&raw mut num` creates a `*mut i32` mutable raw pointer.
Because we created them directly form a local variable, we know these particular raw pointers are valid, but we can't make that assumption about just any raw pointer.
In order to demonstrate this, we will create a raw pointer whose validity we can't be so certain of, using `as` to cast a value instead of using the raw reference operators.
This shows how to create a raw pointer to an arbitrary location in memory.
Trying to use arbitrary memory is undefined: there might be data at that address or there might not, the compiler might optimize the code so there is no memory access, or the program might error with a segmentation fault.
Usually there is no good reason to write code like this, especially in cases where you can use a raw borrow operator instead, but it is possible.
```rust
let address = 0x012345usize;
let r = address as *const i32;
```
Remember that we can create raw pointers in safe code, but we can't *dereference* raw pointers and read the data being pointed to.
Here we use the dereference operator `*` on a raw pointer that requires an `unsafe` block.
```rust
let mut num = 5;
let r1 = &raw const num;
let r2 = &raw mut num;
unsafe {
println!("r1 is: {}", *r1);
println!("r2 is: {}", *r2);
}
```
Creating a pointer does no hard. It is only when we attempt to access the value that it points at that we might end up dealing with an invalid value.
Note in the first and third example, we created `*const i32` and `*mut i32` raw pointers that both pointed to the same memory location where we `num` is stored.
If we instead tried to create an immutable and a mutable reference to `num`, the code would not have compiled because Rust's ownership rules don't allow a mutable reference at the same time as any immutable references.
With raw pointers, we can create a mutable pointer and an immutable pointer to the same location and change data through the mutable pointer, potentially create a data race.
With all of the dangers, why ever use raw pointers?
Once major use case is when interfacing with C code, as you will see in the next section ["Calling an Unsafe Function or Method"]().
Another case is when building up safe abstractions that the borrow checker doesn't understand.
We will introduce unsafe function and then look at an example of a safe abstraction that uses unsafe code.
## Calling an Unsafe Function or Method
The second type of superpower that you can perform in an unsafe block is calling unsafe functions.
Unsafe functions and methods look exactly like regular functions and methods, but they have an extra `unsafe` before the rest of the definitions.
The `unsafe` keyword indicates the function has requirements we need to uphold when we call this function, because Rust can't guarantee we have met these requirements.
By calling an unsafe function within an `unsafe` block, we are saying that we have read this function's documentation and take responsibility for upholding the function's contracts.
This code is an unsafe function named `dangerous` that doesn't do anything in its body
```rust
unsafe fn dangerous() {}
unsafe {
dangerous();
}
```
We must call the `dangerous` function within a separate `unsafe` block.
If we attempted to call `dangerous` without the `unsafe` block, we would get this error
```
$ cargo run
Compiling unsafe-example v0.1.0 (file:///projects/unsafe-example)
error[E0133]: call to unsafe function `dangerous` is unsafe and requires unsafe function or block
--> src/main.rs:4:5
|
4 | dangerous();
| ^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
For more information about this error, try `rustc --explain E0133`.
error: could not compile `unsafe-example` (bin "unsafe-example") due to 1 previous error
```
With the `unsafe` block, we are asserting to Rust that we have read this function's documentation, we understand how to use it properly, and we have verified that we are fulfilling the contract of the function.
To perform unsafe operations in the body of an unsafe function you still need to use an `unsafe` block just as within a regular function, and the compiler will warn you if you forget.
This helps to keep `unsafe` blocks as small as possible, as unsafe operations may not be needed across the whole function body.
### Creating a Safe Abstraction over Unsafe Code
Just because a function contains unsafe code, this does mean we need to mark the entire function as unsafe.
In fact wrapping unsafe is a common abstraction.
For example, let's take a look at the `split_at_mut` function from the std library, which requires some unsafe code.
We will also explore how we might implement it.
This safe method is defined on mutable slices: it takes once slice and makes it two by splitting the slice at the index given as an argument.
Here is how to use `split_at_mut`
```rust
let mut v = vec![1, 2, 3, 4, 5, 6];
let r = &mut v[..];
let (a, b) = r.split_at_mut(3);
assert_eq!(a, &mut [1, 2, 3]);
assert_eq!(b, &mut [4, 5, 6]);
```
We can't implement this function using only safe Rust.
An attempt might look something like this, which will not compile.
For simplicity we will implement `split_at_mut` as a function rather than a method and only for slices of `i32` values rather than for a generic type `T`.
```rust
fn split_at_mut(values: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
let len = values.len();
assert!(mid <= len);
(&mut values[..mid], &mut values[mid..])
}
```
This function first gets the total length of the slice.
Then it asserts that the index given as a parameter is within the slice by checking whether it is less than or equal to the length.
This assertion means that if we pass an index that is greater than the length to split the slice at, the function will panic before it attempts to use that index.
Next we return two mutable slices in a tuple, one form the start of the original slice to the `mid` index and another from `mid` to the end of the slice.
We get this compilation error
```
$ cargo run
Compiling unsafe-example v0.1.0 (file:///projects/unsafe-example)
error[E0499]: cannot borrow `*values` as mutable more than once at a time
--> src/main.rs:6:31
|
1 | fn split_at_mut(values: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
| - let's call the lifetime of this reference `'1`
...
6 | (&mut values[..mid], &mut values[mid..])
| --------------------------^^^^^^--------
| | | |
| | | second mutable borrow occurs here
| | first mutable borrow occurs here
| returning this value requires that `*values` is borrowed for `'1`
|
= help: use `.split_at_mut(position)` to obtain two mutable non-overlapping sub-slices
For more information about this error, try `rustc --explain E0499`.
error: could not compile `unsafe-example` (bin "unsafe-example") due to 1 previous error
```
Rust's borrow checker can't understand that we are borrowing different parts of the slice.
It only knows that we are borrowing from the same slice twice.
Borrowing different parts of a slice fundamentally ok because the two slices aren't overlapping, but Rust isn't smart enough to know this.
When we know code is ok, but Rust doesn't, it is time to reach for unsafe code.
Below shows how to use an `unsafe` block, a raw pointer, and some calls to unsafe functions to make the implementation of `split_at_work`.
```rust
use std::slice;
fn split_at_mut(values: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
let len = values.len();
let ptr = values.as_mut_ptr();
assert!(mid <= len);
unsafe {
(
slice::from_raw_parts_mut(ptr, mid),
slice::from_raw_parts_mut(ptr.add(mid), len - mid),
)
}
}
```
Remember from ["The Slice Type"](), from Ch4 that slices are a pointer to some data and the length of the slice.
We can use the `len` method to get the length of a slice and the `as_mut_ptr` method to access the raw pointer of a slice.
In this case because we have a mutable slice to `i32` values, `as_mut_ptr` returns a raw pointer with the type `*mut i32`, which we have stored in the variable `ptr`.
We keep the assertion that the `mid` index is within the slice.
Next we get to the unsafe code: the `slice::from_raw_parts_mut` function takes a raw pointer and a length, and it creates a slice.
We use this function to create a slice that starts from `ptr` and is `mid` items long.
Next we call the `add` method on `ptr` with `mid` as an argument to get a raw pointer that starts at `mid`, and we create a slice using that pointer and the remaining number of items after `mid` as the length.
The function `slice::from_raw_parts_mut` is unsafe because it takes a raw pointer and must trust that this pointer is valid.
The `add` method on raw pointers is also unsafe, because it must trust that the offset location is also a valid pointer.
We have to put this in an `unsafe` block around our calls to `slice::from_raw_parts_mut` and `add` so we could call them.
By looking at the code and by adding the assertion that `mid` must be less than or equal to `len`.
We can tell that all the raw pointers used within the `unsafe` block will be valid pointers to data within the slice.
This is an acceptable and appropriate use of `unsafe`.
Notice that we don't need to mark the resulting `split_at_mut` function as `unsafe`.
We can call this function form safe Rust.
We have created a safe abstraction to the unsafe code with an implementation of the function that uses `unsafe` code in a safe way, because it creates only valid pointers from the data this function has access to.
By contrast, the use of `slice::from_raw_parts_mut` here would likely crash when the slice is used.
This code takes an arbitrary memory location and creates a slice 10,000 items long.
```rust
use std::slice;
let address = 0x01234usize;
let r = address as *mut i32;
let values: &[i32] = unsafe { slice::from_raw_parts_mut(r, 10000) };
```
We don't own the memory at this arbitrary location and there is no guarantee that the slice this code creates contains valid `i32` values.
Attempting to use `values` as though it's aa valid slice results in undefined behavior.
## Using `extern` Functions to Call External Code
Sometimes, your Rust code might need to interact with code written in another language.
To enable this, Rust has the keyword `extern` that facilitates the creation and use of a *Foreign Function Interface (FFI)*.
An FFI is a way for a programming language to defined functions and enable a different (foreign) programming language to call those functions.
This code demonstrates how to set up an integration with the `abs` function form the C std library.
Functions declared within `extern` blocks are usually unsafe to call from Rust code, so they must also be marked `unsafe`.
The reason is that other languages don't enforce Rust's rules and guarantees, and Rust can't check them, so the responsibility falls on the programmer to ensure safety.
```rust
unsafe extern "C" {
fn abs(input: i32) -> i32;
}
fn main() {
unsafe {
println!("Absolute value of -3 according to C: {}", abs(-3));
}
}
```
Within the `unsafe extern "C"` block, we list the names and signatures of external functions from another language we want to call.
The `"C"` part defines which *application binary interface (ABI)* the external function uses: the ABI defines how to call the function at the assembly level.
The `"C"` ABI is the most common and follows the C programming language's ABI.
This particular function does not have any memory safety considerations.
In fact, we know that any call to `aabs` will always be safe for any `i32`, so we can use the `safe` keyword to say that this specific function is safe to call even though it is in an `unsafe extern` block.
Once we make this change, calling it no longer requires an `unsafe` block.
Here is the updated code
```rust
unsafe extern "C" {
safe fn abs(input: i32) -> i32;
}
fn main() {
println!("Absolute value of -3 according to C: {}", abs(-3));
}
```
Marking a function as `safe` does not inherently make it safe.
Instead, it is like a promise you are making to Rust that it *is* safe.
It is still your responsibility to make sure that the promise is kept.
### Calling Rust Functions form Other Languages
You can also use `extern` to create an interface that allows other languages to call Rust functions.
Instead of creating a whole `extern` block, we add the `extern` keyword and specify the ABI to use before the `fn` keyword for the relevant function.
Additionally we need to add a `#[unsafe(no_mangle)]` annotation to tell the Rust compiler not to mange the name of this function.
*Mangling* is when a compiler changes the name we have given a function to a different name that contains more information for other parts of the compilation process to consume but is less human readable.
Every programming language compiler mangles names slightly differently, so for a Rust function to be nameable by other languages, we must disable the Rust compiler's name mangling.
This is unsafe because there might be name collisions across libraries with the built-in mangling, so it is our responsibility to make sure the name we have exported is safe to export without mangling.
Here we make the `call_from_c` function accessible from C code, after it is compiled to a shared library and linked from C
```rust
#[unsafe(no_mangle)]
pub extern "C" fn call_from_c() {
println!("Just called a Rust function from C!");
}
```
This usage of `extern` does not require `unsafe`
## Accessing or Modifying a Mutable Static Variable
So far we have not talked about `global variables`, which Rust does support but can be problematic with Rust's ownership rules.
If two threads are accessing the same mutable global variable, it can cause a data race.
In Rust, global variables are called *static* variables.
Here shows an example declaration and use of a static variable with a string slice as a value.
```rust
static HELLO_WORLD: &str = "Hello, world!";
fn main() {
println!("name is: {HELLO_WORLD}");
}
```
Static variables are similar to constants, which we discussed in the ["Constants"]() section in Ch3.
The names of static variables are in `SCREAMING_SNAKE_CASE` by convention.
Static variables can only store references with the `'static` lifetime.
This means the Rust compiler can figure out the lifetime and we aren't required to annotate it explicitly.
Accessing an immutable static variable is safe.
A subtle difference between constants and immutable static variables is that values in a static variable have a fixed address in memory.
Using the value will always access the same data.
Constants, on the other hand are allowed to duplicate their data whenever they are used.
Another difference is that static variables can by mutable.
Accessing and modifying mutable static variables is *unsafe*.
Here shows how to declare, access and modify a mutable static variable named `COUNTER`.
```rust
static mut COUNTER: u32 = 0;
/// SAFETY: Calling this from more than a single thread at a time is undefined
/// behavior, so you *must* guarantee you only call it from a single thread at
/// a time.
unsafe fn add_to_count(inc: u32) {
unsafe {
COUNTER += inc;
}
}
fn main() {
unsafe {
// SAFETY: This is only called from a single thread in `main`.
add_to_count(3);
println!("COUNTER: {}", *(&raw const COUNTER));
}
}
```
Just like with regular variables, we specify mutability using the `mut` keyword.
Any code that reads or writes from `COUNTER` must be within an `unsafe` block.
The code above compiles and prints `COUNTER: 3` as expected because it is single threaded.
Having multiple thread access `COUNTER` would likely result in data races, so it is undefined behavior.
Due to this we need to mark the entire function as `unsafe` and document the safety limitation, so anyone calling this function knows what they are and are not allowed to do safely.
Whenever we write an unsafe function, it is idiomatic to write a comment starting with `SAFETY` and explaining what the caller needs to do to call the function safely.
Also whenever we perform an unsafe operation it is idiomatic to write a comment starting with `SAFETY` to explain how the safety rules are upheld.
As well, the compiler will not allow you to create references to a mutable static variable.
You can only access it via a raw pointer, created with one of the raw borrow operators.
This includes in cases where the reference is created invisibly as when it is used in the `println!` in this code listing.
The requirement that references to static mutable variables can only be created via raw pointers helps make the safety requirements for using them more obvious.
With mutable data that is globally accessible, it is difficult to ensure that there are no data races, which is why Rust considers mutable static variables to be unsafe.
Where it is possible it is preferred to use concurrency techniques and thread-safe smart pointers, so the compiler checks that data accessed from different threads is done safely.
## Implementing an Unsafe Trait
Additionally `unsafe` to implement an unsafe trait.
A trait is unsafe when at least one of its methods has some invariant that the compiler can't verify.
We declare that a trait is `unsafe` by adding the `unsafe` keyword before `trait` and marking the implementation of the trait as `unsafe` too.
Here is an example of this
```rust
unsafe trait Foo {
// methods go here
}
unsafe impl Foo for i32 {
// method implementations go here
}
fn main() {}
```
By using `unsafe impl`, we are promising that we will uphold the invariants that the compiler can't verify.
As an example, recall the `Sync` and `Send` marker traits we discussed in ["Extensible Concurrency with the `Sync` and `Send` Traits"]() section.
The compiler implements these traits automatically if our types are composed entirely of `Send` and `Sync` types.
If we implement a type that contains a type that is not `Send` or `Sync`, such as raw pointers, and we want to mark that type as `Send` or `Sync`, we must use `unsafe`.
Rust can't verify that our type upholds the guarantees that it can be safely sent across threads or accessed form multiple threads.
We need to do these checks manually and indicate as such with `unsafe`.
## Accessing Fields of a Union
The last superpower that works on with `unsafe` is accessing fields of a *union*.
A `union` is similar to a `struct`, but only one declared field is used in a particular instance at one time.
Unions are primarily used to interface with unions in C code.
Accessing union fields is unsafe because Rust can't guarantee the type of data currently being stored in the union instance.
You can learn more about unions in [the Rust Reference](https://doc.rust-lang.org/reference/items/unions.html).
## Using Miri to check unsafe code
When writing unsafe code, you may want to check that you have written actually is safe and correct.
Using [Miri](https://github.com/rust-lang/miri), an official Rust tool for detecting undefined behavior is one of the best ways to do it.
While the borrow checker is a *static* tool which works at compile time, Miri is a *dynamic* tool which works at runtime.
It checks your code by running your program or its test suite and detecting when you violate the rules it understands about how Rust should work.
Miri requires a nightly build of Rust (discussed in [Appendix G: How Rust is Made and "Nightly Rust"](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html)).
You can install both a nightly version of Rust and the Miri tool by typing `rustup +nightly component add miri`.
This does not change what version of Rust your project uses.
This only adds the tool to your system so you can use it when you want to.
You can run Miri on a project by typing cargo `+nightly miri run` or `cargo +nightly miri test`.
Here is an example of how useful when we run it on a previous example
```
$ cargo +nightly miri run
Compiling unsafe-example v0.1.0 (file:///projects/unsafe-example)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
Running `/Users/chris/.rustup/toolchains/nightly-aarch64-apple-darwin/bin/cargo-miri runner target/miri/aarch64-apple-darwin/debug/unsafe-example`
warning: creating a shared reference to mutable static is discouraged
--> src/main.rs:14:33
|
14 | println!("COUNTER: {}", COUNTER);
| ^^^^^^^ shared reference to mutable static
|
= note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html>
= note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives
= note: `#[warn(static_mut_refs)]` on by default
COUNTER: 3
```
It helpfully and correctly notices that we have shared references to mutable data and warns about it.
Here it does not tell us how to fix the problem, but it mean that we know there is a possible issue and can think about how to make sure it is safe.
In other cases, it can actually tell us that some code is *sure* to be wrong and make recommendations about how to fix it.
Miri will not catch *everything* you may get wrong when writing unsafe code.
Since it is a dynamic check, it only catches problems with code that actually gets run.
This means you will need to use it with good testing techniques to increase your confidence about the unsafe code you have written.
Additionally it does not cover every possible way your code can be unsound.
If Miri *does* catch a problem, you know that there is a bug, but just because Miri *doesn't* catch a bug doesn't mean there isn't a problem.
Miri can catch a lot despite this.
## When to Use Unsafe Code
Using `unsfe` to take one of the five superpowers, isn't wrong or frowned upon.
But it is trickier to get `unsafe` code correct because the compiler can't help uphold memory safety.
When you have a reason to use `unsafe` code, you can do so and having the explicit `unsafe` annotation makes it easier to tack down the source of problems when they occur.
When you write unsafe code, you can use Miri to help to be more confident that the code you wrote upholds Rust's rules.
For a deeper exploration of how to work effectively with unsafe Rust, read Rust's official guide on the subject the [Rustonomicon](https://doc.rust-lang.org/nomicon/).

View File

@ -1 +1 @@
{"rustc_fingerprint":953660965870803700,"outputs":{"13331785392996375709":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/brock/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.85.1 (4eb161250 2025-03-15)\nbinary: rustc\ncommit-hash: 4eb161250e340c8f48f66e2b929ef4a5bed7c181\ncommit-date: 2025-03-15\nhost: x86_64-unknown-linux-gnu\nrelease: 1.85.1\nLLVM version: 19.1.7\n","stderr":""},"2063776225603076451":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/brock/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""}},"successes":{}}
{"rustc_fingerprint":517133967738620428,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.86.0 (05f9846f8 2025-03-31)\nbinary: rustc\ncommit-hash: 05f9846f893b09a1be1fc8560e33fc3c815cfecb\ncommit-date: 2025-03-31\nhost: x86_64-pc-windows-msvc\nrelease: 1.86.0\nLLVM version: 19.1.7\n","stderr":""},"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\Brock\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"pc\"\nwindows\n","stderr":""}},"successes":{}}

11
hello/404.html Normal file
View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Hello!</title>
</head>
<body>
<h1>Oops!</h1>
<p>Sorry, I don't know what you're asking for.</p>
</body>
</html>

7
hello/Cargo.lock generated Normal file
View File

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "hello"
version = "0.1.0"

6
hello/Cargo.toml Normal file
View File

@ -0,0 +1,6 @@
[package]
name = "hello"
version = "0.1.0"
edition = "2024"
[dependencies]

11
hello/hello.html Normal file
View File

@ -0,0 +1,11 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<title>Hello!</title>
</head>
<body>
<h1>Hello!</h1>
<p>Hi from Rust</p>
</body>
</html>

82
hello/src/lib.rs Normal file
View File

@ -0,0 +1,82 @@
use std::{ sync::{ mpsc, Mutex, Arc }, thread };
pub struct ThreadPool {
workers: Vec<Worker>,
sender: Option<mpsc::Sender<Job>>,
}
impl ThreadPool {
/// Create a new ThreadPool.
///
/// The size is the number of threads in the pool.
///
/// # Panics
///
/// The `new` function will panic if the size is zero.
pub fn new(size: usize) -> ThreadPool {
assert!(size > 0);
let (sender, receiver) = mpsc::channel();
let receiver = Arc::new(Mutex::new(receiver));
let mut workers = Vec::with_capacity(size);
for id in 0..size {
workers.push(Worker::new(id, Arc::clone(&receiver)));
}
ThreadPool { workers, sender: Some(sender) }
}
pub fn execute<F>(&self, f: F) where F: FnOnce() + Send + 'static {
let job = Box::new(f);
self.sender.as_ref().unwrap().send(job).unwrap();
}
// ambitious side project
// pub fn build(size: usize) -> Result<ThreadPool, PoolCreationError> {}
}
impl Drop for ThreadPool {
fn drop(&mut self) {
drop(self.sender.take());
for worker in &mut self.workers.drain(..) {
println!("Shutting down worker {}", worker.id);
worker.thread.join().unwrap();
}
}
}
struct Worker {
id: usize,
thread: thread::JoinHandle<()>,
}
impl Worker {
fn new(id: usize, receiver: Arc<Mutex<mpsc::Receiver<Job>>>) -> Worker {
let thread = thread::spawn(move || {
loop {
let message = receiver.lock().unwrap().recv();
match message {
Ok(job) => {
println!("Worker {id} got a job; executing");
job();
}
Err(_) => {
println!("Worker {id} disconnected; shutting down.");
break;
}
}
}
});
Worker { id, thread }
}
}
type Job = Box<dyn FnOnce() + Send + 'static>;

74
hello/src/main.rs Normal file
View File

@ -0,0 +1,74 @@
use std::{
fs,
io::{ prelude::*, BufReader },
net::{ TcpListener, TcpStream },
thread,
time::Duration,
};
use hello::ThreadPool;
fn main() {
// this is not related I was just curious
// let test = fs::read_to_string("test.yaml").unwrap();
// println!("{}", test);
let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
let pool = ThreadPool::new(4);
for stream in listener.incoming().take(2) {
let stream = stream.unwrap();
pool.execute(|| {
handle_connection(stream);
});
}
println!("Shutting down.");
}
fn handle_connection(mut stream: TcpStream) {
let buf_reader = BufReader::new(&stream);
// let http_request: Vec<_> = buf_reader
// .lines()
// .map(|result| result.unwrap())
// .take_while(|line| !line.is_empty())
// .collect();
let request_line = buf_reader.lines().next().unwrap().unwrap();
// no longer needed, here for debug and being useful
// println!("Request: {:#?}", http_request);
// if request_line == "GET / HTTP/1.1" {
// let status_line = "HTTP/1.1 200 OK";
// let contents = fs::read_to_string("./hello.html").unwrap();
// let length = contents.len();
// let res = format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}");
// stream.write_all(res.as_bytes()).unwrap();
// } else {
// let status_line = "HTTP/1.1 404 NOT FOUND";
// let contents = fs::read_to_string("404.html").unwrap();
// let length = contents.len();
// let res = format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}");
// stream.write_all(res.as_bytes()).unwrap();
// }
let (status_line, filename) = match &request_line[..] {
"GET / HTTP/1.1" => ("HTTP/1.1 200 OK", "hello.html"),
"GET /sleep HTTP/1.1" => {
thread::sleep(Duration::from_secs(5));
("HTTP/1.1 200 OK", "hello.html")
}
_ => ("HTTP/1.1 404 NOT FOUND", "404.html"),
};
let contents = fs::read_to_string(filename).unwrap();
let length = contents.len();
let res = format!("{status_line}\r\nContent-Length: {length}\r\n\r\n{contents}");
stream.write_all(res.as_bytes()).unwrap();
}

41
hello/test.yaml Normal file
View File

@ -0,0 +1,41 @@
Diamond Tier:
- Schulich School of Engineering:
LogoUrl: https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQj9L3ZGK6WtOmJbzxmCzRxwLXYKGC5SDcAKHb0ScfbUmbtG0IujQt6eQDaI_Pm9g4DZvc&usqp=CAU
Url: https://schulich.ucalgary.ca/
DescriptionAboutSponsor: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce eu magna in diam consectetur rhoncus vel nec turpis. Sed finibus mi eu sem varius faucibus. Donec semper erat et bibendum pharetra. Suspendisse cursus lorem sed nisi semper, a rutrum nunc luctus. Nunc ullamcorper enim id orci interdum ultrices. Donec vestibulum nunc vel nisl pretium tempus. Morbi quis ante et ligula eleifend eleifend. Proin bibendum maximus elit vitae congue. Vivamus egestas, ex in tempor posuere, ligula nunc iaculis massa, in imperdiet dui justo eu dolor. Nullam placerat velit quis sem mattis, laoreet pharetra elit tempor.
Platinum Tier:
- Platinum Sponsor Name:
LogoUrl:
Url:
DescriptionAboutSponsor:
Gold Tier:
- Suri:
LogoUrl: https://lh5.googleusercontent.com/WJsBsmcLypQhY0MMOLQtJSGFXrLQqPKNc3502rYUGKPCq_SfS9CxuoB3n541Kn9bKPm2b5aixCnYsCVYZAts2Y8xvmOHWL3nnbKtWUkE1KoFYYQ4bXUlikfF0NPIynxhzQ=w1280
Url: https://www.surimanufacturing.com/
DescriptionAboutSponsor:
- SKF:
LogoUrl: https://www.skf.com/v2/assets/img/skf-logo-white.svg
Url: https://www.skf.com/ca/en
DescriptionAboutSponsor: I am not sure
- WRMA:
LogoUrl: https://wildrosemx.com/wp-content/uploads/2021/08/wild-rose-motocross-calgary-rasterized.png
Url: https://wildrosemx.com/
DescriptionAboutSponsor:
Silver Tier:
- Encore Metals:
LogoUrl: https://www.encoremetals.com/assets/images/logos/encore-metals-logo.png
Url: https://www.encoremetals.com/
DescriptionAboutSponsor: Metal supplier
- CNOOC:
LogoUrl: https://cnoocinternational.com/img/cnooc-logo.png
Url: https://cnoocinternational.com/
DescriptionAboutSponsor:
Bronze Tier:
- Redbull:
LogoUrl: "https://img.redbull.com/redbullcom/static/redbullcom-logo_double-with-text.svg"
Url: https://www.redbull.com/ca-en/
DescriptionAboutSponsor:
- Canada Action:
LogoUrl:
Url:
DescriptionAboutSponsor:

View File

@ -1 +1 @@
{"rustc_fingerprint":953660965870803700,"outputs":{"13331785392996375709":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/brock/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"2063776225603076451":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/brock/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.85.1 (4eb161250 2025-03-15)\nbinary: rustc\ncommit-hash: 4eb161250e340c8f48f66e2b929ef4a5bed7c181\ncommit-date: 2025-03-15\nhost: x86_64-unknown-linux-gnu\nrelease: 1.85.1\nLLVM version: 19.1.7\n","stderr":""}},"successes":{}}
{"rustc_fingerprint":517133967738620428,"outputs":{"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.86.0 (05f9846f8 2025-03-31)\nbinary: rustc\ncommit-hash: 05f9846f893b09a1be1fc8560e33fc3c815cfecb\ncommit-date: 2025-03-31\nhost: x86_64-pc-windows-msvc\nrelease: 1.86.0\nLLVM version: 19.1.7\n","stderr":""},"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\Brock\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"pc\"\nwindows\n","stderr":""}},"successes":{}}