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
* Devle a bit deeper on the why and the how of generic configs
* Fix a typo (wrong word used)
* Typo: Rename the 'more configurable' paragraph title to a more suitable one, Fix: Move config trait impl in the runtime paragraph to a more suitable location. Fix verbage/info. Fix: Better titles
* Update steps/33/README.md
* spaces to tabs
* suggested changes to first section
* Update README.md
* proof
---------
Co-authored-by: Shawn Tabrizi <[email protected]>
Copy file name to clipboardExpand all lines: steps/33/README.md
+55-6Lines changed: 55 additions & 6 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -25,7 +25,7 @@ The more obvious use of traits is to define custom functions.
25
25
26
26
Let's say we want to expose a function which returns the name of something.
27
27
28
-
You could a trait `GetName`:
28
+
You could create a trait `GetName`:
29
29
30
30
```rust
31
31
pubtraitGetName {
@@ -39,7 +39,7 @@ Then you could implement this trait for any object.
39
39
structShawn;
40
40
implGetNameforShawn {
41
41
fnname() ->String {
42
-
return"shawn".to_string();
42
+
"shawn".to_string()
43
43
}
44
44
}
45
45
```
@@ -59,7 +59,7 @@ We won't actually use this feature of traits in our simple blockchain, but there
59
59
60
60
The other thing you can do with traits is define Associated Types.
61
61
62
-
This is covered in [chapter 19 of the Rust Book](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html) under "Advance Traits".
62
+
This is covered in [chapter 19 of the Rust Book](https://doc.rust-lang.org/book/ch19-03-advanced-traits.html) under "Advanced Traits".
63
63
64
64
Let's learn this concept by first looking at the problem we are trying to solve.
65
65
@@ -112,13 +112,13 @@ Let's try to understand this syntax real quick.
112
112
- `T::BlockNumber`
113
113
- `T::Nonce`
114
114
115
-
There is no meaningful difference between what we had before with 3 generic parameters, and a single generic parameter represented by a `Config` trait, but it certainly makes everything more scalable, easy to read, and easy to configure.
115
+
While this may seem like a purely stylistic change, it enforces a powerful constraint: for any given type implementing `Config` (like our `Runtime`), there can only be one corresponding `AccountId`, `BlockNumber`, and `Nonce`. This guarantees type consistency across the pallet.
116
116
117
117
In this context, we call the trait `Config` because it is used to configure all the types for our Pallet.
118
118
119
119
### Implementing the Config Trait
120
120
121
-
Let's round this out with showing how you can actually implement and use the `Config` trait.
121
+
Let's round this out by showing how you can actually implement and use the `Config` trait.
122
122
123
123
Just like before, we need some object which will implement this trait. In our case, we can use the `Runtime` struct itself.
124
124
@@ -140,6 +140,55 @@ pub struct Runtime {
140
140
141
141
Here we are basically saying that `Pallet` will use `Runtime` as its generic type, but this is defined within the `Runtime`, so we refer to it as `Self`.
142
142
143
+
## The Power of Associated Types
144
+
145
+
Using traits with associated types does more than just make the code less verbose; it fundamentally changes how we can configure our blockchain system.
146
+
147
+
1. It creates a single place where all types that our blockchain system requires are defined: the implementation of the `Config` trait. As long as you use this single implementation, you ensure type consistency across your entire runtime.
148
+
149
+
2. It allows us to avoid hardcoding specific types, like `String` or `u32`, and instead use generic data types, like `T::AccountId` and `T::BlockNumber`. This allows us to swap out or change the final configured types in the runtime without changing any of the pallet code.
150
+
151
+
3. It allows us to create a single spot for us to configure those final types. You can see that we use the `mod types {}` section in `main.rs` to define all the concrete types in one place, so they can be consistently referenced. Without this, you might accidentally use different concrete types in different pallets (like `u32` in one place and `u64` in another), causing compilation errors or, worse, subtle bugs.
152
+
153
+
4. Finally, it allows us to configure different runtimes with completely different concrete types. This is an extremely powerful and often used feature in Substrate / the Polkadot-SDK.
154
+
155
+
For example, we can create two runtime configurations, one for production and one for testing, and configure them completely differently:
156
+
157
+
```rust
158
+
// In a production runtime
159
+
struct Runtime;
160
+
impl my_pallet::Config for Runtime {
161
+
type AccountId = AccountId32;
162
+
type BlockNumber = u32;
163
+
type Nonce = u64;
164
+
// etc...
165
+
}
166
+
167
+
// In a test runtime
168
+
struct TestConfig;
169
+
impl my_pallet::Config for TestConfig {
170
+
type AccountId = String;
171
+
type BlockNumber = u16;
172
+
type Nonce = u8;
173
+
// etc...
174
+
}
175
+
```
176
+
177
+
### The Runtime is a Configuration Hub
178
+
179
+
You should be able to see a bigger idea forming based on how we have designed our blockchain system.
180
+
181
+
You don't *program* the runtime, you **configure** it.
182
+
183
+
- We have designed our blockchain system with reusable pieces of application logic: the pallets.
184
+
- Each of these pallets are written generically, and are entirely configurable.
185
+
- Our runtime includes all of the pallets and pieces of logic it wants to use.
186
+
- Our runtime aggregates and propagates all the expected types for our blockchain system.
187
+
188
+
This pattern allows pallets to be completely independent and reusable. A pallet doesn't need to know which other pallets exist in the runtime, it only needs its `Config` trait implemented. This means you can mix and match different pallets in different runtimes (production, test, development) without modifying the pallet code itself.
189
+
190
+
This is the difference between building a single purpose blockchain and building a blockchain SDK, allowing for reusable, modular components, and ultimately bootstrapping a powerful ecosystem of blockchain developers.
191
+
143
192
## Make Your System Configurable
144
193
145
194
Phew. That was a lot.
@@ -160,4 +209,4 @@ You will have the opportunity to do this whole process again for the Balances Pa
160
209
161
210
Really take time to understand this step, what is happening, and what all of this syntax means to Rust.
162
211
163
-
Remember that Rust is a language which is completely type safe, so end of the day, all of these generic types and configurations need to make sense to the Rust compiler.
212
+
Remember that Rust is a language which is completely type safe, so at the end of the day, all of these generic types and configurations need to make sense to the Rust compiler.
0 commit comments