You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Notice how we're grabbing different ranges of `transaction_bytes`. We have to repeatedly reference `transaction_bytes` and we have to keep track of the start and end indexes for each component. This is not ideal. Transactions are presented in hex format for a reason. They are designed to be serialized as byte streams that can be transmitted over the network and read one byte at a time in order.
12
-
13
-
One way to read a byte stream is to leverage Rust's standard library's [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) trait. The slice data type in Rust implements the `Read` trait. What does this mean? Well, as we will see, it gives us a method, `read`, which will read some bytes from the slice and then store that data into a array. When we call `read` again, it will start from where it left off. In other words, it keeps track of where we are in the stream and modifies the pointer as it reads. This means we don't need to keep track of any indexes.
12
+
Notice how we're grabbing different ranges of `transaction_bytes`.
13
+
We have to repeatedly reference `transaction_bytes` and we have to keep track of the start and end indexes for each component.
14
+
This is not ideal because we can easily make mistakes.
15
+
16
+
*Note: there's an indexing mistake in the code above, can you see what it is?*
17
+
<!--
18
+
Since vec[0..4] notation is not inclusive to the end of the index, we are skipping one byte: the element at index 4.
19
+
-->
20
+
21
+
Transactions are presented in hex format for a reason.
22
+
They are designed to be serialized as byte streams that can be transmitted over the network and read one byte at a time in order.
23
+
A better solution would be to use a function that keeps track of the indices and allows us to request the number of bytes we require.
24
+
Rust's standard library's [`Read`](https://doc.rust-lang.org/std/io/trait.Read.html) trait allows for exactly this.
25
+
The slice data type in Rust implements the `Read` trait.
26
+
What does this mean? It gives us a method, `read`, which will read some bytes from the slice and return that data in an array.
27
+
When we call `read` again, it will start from where it left off.
28
+
In other words, the `read` trait includes the machinery to keep track of the current position we are reading in the stream and to manage the pointer as it proceeds.
29
+
This means we don't need to keep track of any indexes ourselves.
14
30
15
31
Let's walk through how this works at a high level with a quick example and then dive deeper into what traits are and how they work.
16
32
17
-
In order to use a trait method we have to first bring it into scope with a `use` statement. In this case, we want to bring the `Read` trait into scope with `use std::io::Read`. The next thing we want to do is use the `read` method as intended based on the example from the [documentation](https://doc.rust-lang.org/std/io/trait.Read.html#examples).
33
+
In order to use a trait method we have to first bring it into scope with a `use` statement.
34
+
In this case, we want to bring the `Read` trait into scope with `use std::io::Read`.
35
+
The next thing we want to do is use the `read` method as intended based on the example from the [documentation](https://doc.rust-lang.org/std/io/trait.Read.html#examples).
18
36
19
37
You can follow along with this example in [Rust Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=627e8e7c10d530819a80f189cedded13).
20
38
```rust
@@ -28,28 +46,38 @@ fn main() {
28
46
letversion=u32::from_le_bytes(buffer);
29
47
30
48
println!("Version: {}", version);
49
+
println!("Bytes slice: {:?}", bytes_slice);
31
50
}
32
51
```
33
52
34
-
The `mut` keyword before `bytes_slice` tells Rust the variable is mutable. If we don't provide that keyword in a variable declaration, then the compiler will complain that we're attempting to change an immutable variable, which is not allowed.
53
+
The `mut` keyword before `bytes_slice` tells Rust the variable is mutable.
54
+
If we don't provide that keyword in a variable declaration, then the compiler will complain that we're attempting to change the value of an immutable variable, which is not allowed.
35
55
36
-
You might also notice the `&mut` keyword in the argument to the `read` method. This indicates that we're passing in `buffer` as a *mutable reference*. We'll talk more about this means in the next chapter so for now let's not worry about that nuance.
56
+
You might also notice the `&mut` keyword in the argument to the `read` method.
57
+
This indicates that we're passing in `buffer` as a *mutable reference*.
58
+
We'll talk more about this means in the next chapter so for now let's not worry about that nuance.
37
59
38
60
When we run this, it will print the following:
39
61
```console
40
62
Version: 1
41
63
Bytes slice: [2]
42
64
```
43
65
44
-
And this is what we'd expect. The Version is `1`. And the `bytes_slice` variable has been updated and no longer contains the first 4 bytes.
66
+
And this is what we'd expect.
67
+
The Version is `1` and the `bytes_slice` variable has been updated and no longer contains the first 4 bytes.
45
68
46
-
You may notice that the way this works is that you have to first create an array with a fixed size. Calling `read` will then extract the number of bytes equal to the size of the array, store that into a buffer and then update our slice.
69
+
You may notice that the way this works is that you have to first create an array with a fixed size.
70
+
Calling `read` will then extract the number of bytes equal to the size of the array, store that into a buffer and then update our slice.
47
71
48
-
## Traits Explanation
72
+
## What are traits?
49
73
50
-
So what are traits exactly? Traits are a way to define shared behavior. You can think of them as a template for a particular set of behaviors. For example, the `Read` trait provides a template for types that want to "read data". It lays out what to expect and what types of functions are available.
74
+
Traits are a way to define shared behavior.
75
+
You can think of them as a template for a particular set of behaviors.
76
+
For example, the `Read` trait provides a template for types that want to "read data".
77
+
It lays out an *abstract interface* for a type: what kind of behavior is expected from the type and which functions are available to exercise that behavior.
51
78
52
-
Let's take a closer look at the `Read` trait [from the documentation](https://doc.rust-lang.org/std/io/trait.Read.html). It has a required method, `read`, which has the following function signature: `fn read(&mut self, buf: &mut [u8]) -> Result<usize>;`.
79
+
Let's take a closer look at [the documentation for the `Read` trait](https://doc.rust-lang.org/std/io/trait.Read.html).
80
+
It defines a required method, `read`, which has the following function signature: `fn read(&mut self, buf: &mut [u8]) -> Result<usize>;`.
53
81
54
82
```rust
55
83
...
@@ -61,13 +89,20 @@ pub trait Read {
61
89
...
62
90
```
63
91
64
-
The `read` method itself is not actually implemented with any logic. You'll notice there's no function body, just the signature. The types that "implement" this trait are expected to provide the function logic for any *required* method, or trait methods that have no implementation.
92
+
You'll notice there's no function body, just the signature.
93
+
It means the `read` method itself is not actually implemented with any logic in the trait declaration.
94
+
We expect the types that "implement" this trait to aactually provide the function logic for any *required* method, or trait methods, that have no implementation.
65
95
66
-
A trait can also provide other methods that a type can get access to once it has implemented the trait. These are known as *provided* methods and are considered *default* implementations since they can also be overwritten. You'll notice for example that there is a [`read_exact` method](https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact) which is implemented with a call to the [`default_read_exact`](https://doc.rust-lang.org/src/std/io/mod.rs.html#558) method.
96
+
A trait can also provide other methods that a type can get access to once it has implemented the trait.
97
+
These are known as *provided* methods and are considered *default* implementations since they can also be overwritten.
98
+
You'll notice, for example, that there is a [`read_exact` method](https://doc.rust-lang.org/std/io/trait.Read.html#method.read_exact) which is implemented with a call to the [`default_read_exact`](https://doc.rust-lang.org/src/std/io/mod.rs.html#558) method.
99
+
`default_read_exact` by itself is implemented with a call the the `read` method.
67
100
68
-
As long as a type implements the `Read` trait by providing a `read` method, it will have access to these other *provided* methods. A type can also choose to override some or all of these *provided* methods as well and have its own implementations.
101
+
As long as a type implements the `Read` trait by providing a `read` method, it will have access to these other *provided* methods.
102
+
A type can also choose to override some or all of these *provided* methods as well and have its own custom implementations (e.g. for performance reasons).
69
103
70
-
Now if we look at the `slice` type from the documentation, we can see that it [*implements* the `Read` trait](https://doc.rust-lang.org/std/primitive.slice.html#impl-Read-for-%26%5Bu8%5D) and provides the function logic for the `read` method. Let's take a look at [the source code](https://doc.rust-lang.org/src/std/io/impls.rs.html#235-250):
104
+
Now if we look at the `slice` type documentation, we can see that it [*implements* the `Read` trait](https://doc.rust-lang.org/std/primitive.slice.html#impl-Read-for-%26%5Bu8%5D) and provides the function logic for the `read` method.
105
+
Let's take a look at [the source code](https://doc.rust-lang.org/src/std/io/impls.rs.html#235-250):
71
106
72
107
```rust
73
108
...
@@ -95,14 +130,20 @@ impl Read for &[u8] {
95
130
...
96
131
```
97
132
98
-
Don't worry if you don't understand what all of this means just yet! Simply notice how we *implement* a trait with the `impl` keyword. So `impl Read for &[u8]` is the code block that provides the function logic for the trait. The other thing to notice is how the function signature for `read` matches the trait's function signature.
133
+
Don't worry if you don't understand what all of this means just yet!
134
+
Simply notice how we *implement* a trait with the `impl` keyword.
135
+
So `impl Read for &[u8]` is the code block that provides the function logic for the trait.
136
+
The other thing to notice is how the function signature for `read` matches the trait's function signature.
99
137
100
-
The idea here is that different types, not just the `&[u8]` type can implement the `Read` trait by providing the function logic for any required method and then be expected to have similar behavior and get access to the trait's provided methods.
138
+
The idea here is that different types, not just the `&[u8]` type can implement the `Read` trait by providing the function logic for any required method and then be expected to have similar behavior and get access to the trait's provided methods.
101
139
The function logic itself for each type might differ, but given the template they are expected to take in the same arguments, return the same type and generally do the same thing, which in this case is to read some data and modify `self` and the buffer.
102
140
103
-
Again, you might notice some patterns in the code above that you are not yet familiar with, such as the `&mut` keyword and asterisk `*` before `self` at the bottom of the function. We'll go into more detail about what these mean in the next lesson.
141
+
Again, you might notice some patterns in the code above that you are not yet familiar with, such as the `&mut` keyword and asterisk `*` before `self` at the bottom of the function.
142
+
We'll go into more detail about what these mean in the next lesson.
104
143
105
-
Let's now update our program to print out the version number leveraging the `Read` trait. We can convert the `transaction_bytes``Vec` to a `slice` type using the `as_slice` method. Here is the modified `read_version` function.
144
+
Let's now update our program to print out the version number leveraging the `Read` trait.
145
+
We can convert the `transaction_bytes``Vec` to a `slice` type using the `as_slice` method.
And voila, this will print `Version: 1` as expected! Great job so far!
168
+
And voila, this will print `Version: 1` as expected!
169
+
Great job so far!
128
170
129
-
How do we grab the modified `bytes_slice` and continue decoding the transaction? What we probably want to do is pass in the `bytes_slice` into this function as an argument and continue using it in the `main` function. We'll talk more about that and associated Rust concepts of references and borrowing in the next section.
171
+
How do we grab the modified `bytes_slice` and continue decoding the transaction?
172
+
What we probably want to do is pass in the `bytes_slice` into this function as an argument and continue using it in the `main` function.
173
+
We'll talk more about that and associated Rust concepts of references and borrowing in the next section.
130
174
131
175
### Quiz
132
-
1.*Take another look at the `Read` trait and the implementation of the `Read` trait for a slice in the documentation. What are the required and provided methods for the trait? What provided methods are being overwritten by the slice?*
176
+
1.*Take another look at the `Read` trait and the implementation of the `Read` trait for a slice in the documentation.
177
+
What are the required and provided methods for the trait?
178
+
What provided methods are being overwritten by the slice?*
133
179
2.*Consider the following block of code in which we create a Vec and then attempt to print it out:*
134
180
```rust
135
181
fnmain() {
136
182
letvec:Vec::<u8> =vec![0, 0, 0, 0, 0];
137
183
println!("Vec: {}", vec);
138
184
}
139
185
```
186
+
140
187
*The compiler will return an error that the Vec cannot be formatted with the default formatter.*
141
188
*1. Which trait is not implemented for the Vec that is required for it to be printed?*
142
189
*2. How else can you print out the vector for debugging purposes?*
0 commit comments