Skip to content

Commit 39fa0fa

Browse files
Config Description Enhancement (#25)
* 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]>
1 parent 3ccea43 commit 39fa0fa

File tree

2 files changed

+56
-7
lines changed

2 files changed

+56
-7
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Once you have made changes to the appropriate branch, you can use these commands
5050

5151
- Convert `master` to an up to date `gitorial` branch:
5252

53-
```sh
53+
```sh
5454
gitorial-cli repack -p /path/to/rust-state-machine -i master -s steps -o gitorial2
5555
```
5656

steps/33/README.md

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ The more obvious use of traits is to define custom functions.
2525

2626
Let's say we want to expose a function which returns the name of something.
2727

28-
You could a trait `GetName`:
28+
You could create a trait `GetName`:
2929

3030
```rust
3131
pub trait GetName {
@@ -39,7 +39,7 @@ Then you could implement this trait for any object.
3939
struct Shawn;
4040
impl GetName for Shawn {
4141
fn name() -> String {
42-
return "shawn".to_string();
42+
"shawn".to_string()
4343
}
4444
}
4545
```
@@ -59,7 +59,7 @@ We won't actually use this feature of traits in our simple blockchain, but there
5959

6060
The other thing you can do with traits is define Associated Types.
6161

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".
6363

6464
Let's learn this concept by first looking at the problem we are trying to solve.
6565

@@ -112,13 +112,13 @@ Let's try to understand this syntax real quick.
112112
- `T::BlockNumber`
113113
- `T::Nonce`
114114

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.
116116

117117
In this context, we call the trait `Config` because it is used to configure all the types for our Pallet.
118118

119119
### Implementing the Config Trait
120120

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.
122122

123123
Just like before, we need some object which will implement this trait. In our case, we can use the `Runtime` struct itself.
124124

@@ -140,6 +140,55 @@ pub struct Runtime {
140140

141141
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`.
142142

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+
143192
## Make Your System Configurable
144193

145194
Phew. That was a lot.
@@ -160,4 +209,4 @@ You will have the opportunity to do this whole process again for the Balances Pa
160209

161210
Really take time to understand this step, what is happening, and what all of this syntax means to Rust.
162211

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

Comments
 (0)