Derive Debug
In Rust, derive
macros provide a convenient way to automatically implement trait functionality for custom data structures.
Macros
In the most simple terms, Macros are rust code that write more rust code.
Macros can make your code easier to read, help avoid repetition, and even let you create your own special rules for coding in Rust.
We will be using (but not writing) macros heavily near the end of this tutorial, and you will see how powerful they can be.
For now, treat them as "magic".
Traits
Think of traits in Rust as shared rules for different types. They allow you to define a set of things that types must be able to do. This way, you can make sure different parts of your code follow the same rules.
Take a look at this example or re-read the Rust Book if you need a refresher on Traits.
We will make and use custom traits later in this tutorial, but know for this step that #[derive(Debug)]
is a macro which implements the Debug
trait for your custom types.
Debug Trait
The Debug trait in Rust is part of the standard library and is used to print and format values for debugging purposes. It provides a default implementation through the `#[derive(Debug)] annotation.
For example:
#![allow(unused)] fn main() { #[derive(Debug)] pub struct MyStruct { field1: i32, field2: String, } }
With the Debug
trait derived, you can now print the struct
to console:
#![allow(unused)] fn main() { let my_instance = MyStruct { field1: 42, field2: "Hello".to_string() }; println!("{:#?}", my_instance); }
The characters :#?
help format the output to make it more readable.
Derive the Debug Trait for Your Runtime
This is a very simple, but helpful step!
We want to be able to print out the current state of our Runtime
at the end of our main
to allow us to easily inspect what it looks like and that everything is functioning as we expect.
To do this, we need to add #[derive(Debug)]
to the struct Runtime
.
However... struct Runtime
is composed of system::Pallet
and balances::Pallet
, so these structs ALSO need to implement the Debug trait.
Complete the TODO
s across the different files in your project and print out your final runtime at the end of the main
function.
You can use cargo run
to see the output of your println
. Everything should compile and run without warnings.
#![allow(unused)] fn main() { use std::collections::BTreeMap; /// This is the Balances Module. /// It is a simple module which keeps track of how much balance each account has in this state /// machine. /* TODO: Add the derive macro to implement the `Debug` trait for `Pallet`. */ pub struct Pallet { // A simple storage mapping from accounts (`String`) to their balances (`u128`). balances: BTreeMap<String, u128>, } impl Pallet { /// Create a new instance of the balances module. pub fn new() -> Self { Self { balances: BTreeMap::new() } } /// Set the balance of an account `who` to some `amount`. pub fn set_balance(&mut self, who: &String, amount: u128) { self.balances.insert(who.clone(), amount); } /// Get the balance of an account `who`. /// If the account has no stored balance, we return zero. pub fn balance(&self, who: &String) -> u128 { *self.balances.get(who).unwrap_or(&0) } /// Transfer `amount` from one account to another. /// This function verifies that `from` has at least `amount` balance to transfer, /// and that no mathematical overflows occur. pub fn transfer( &mut self, caller: String, to: String, amount: u128, ) -> Result<(), &'static str> { let caller_balance = self.balance(&caller); let to_balance = self.balance(&to); let new_caller_balance = caller_balance.checked_sub(amount).ok_or("Not enough funds.")?; let new_to_balance = to_balance.checked_add(amount).ok_or("Overflow")?; self.balances.insert(caller, new_caller_balance); self.balances.insert(to, new_to_balance); Ok(()) } } #[cfg(test)] mod tests { #[test] fn init_balances() { let mut balances = super::Pallet::new(); assert_eq!(balances.balance(&"alice".to_string()), 0); balances.set_balance(&"alice".to_string(), 100); assert_eq!(balances.balance(&"alice".to_string()), 100); assert_eq!(balances.balance(&"bob".to_string()), 0); } #[test] fn transfer_balance() { let mut balances = super::Pallet::new(); assert_eq!( balances.transfer("alice".to_string(), "bob".to_string(), 51), Err("Not enough funds.") ); balances.set_balance(&"alice".to_string(), 100); assert_eq!(balances.transfer("alice".to_string(), "bob".to_string(), 51), Ok(())); assert_eq!(balances.balance(&"alice".to_string()), 49); assert_eq!(balances.balance(&"bob".to_string()), 51); assert_eq!( balances.transfer("alice".to_string(), "bob".to_string(), 51), Err("Not enough funds.") ); } } }
mod balances; mod system; // This is our main Runtime. // It accumulates all of the different pallets we want to use. /* TODO: Add the derive macro to implement the `Debug` trait for `Runtime`. */ pub struct Runtime { system: system::Pallet, balances: balances::Pallet, } impl Runtime { // Create a new instance of the main Runtime, by creating a new instance of each pallet. fn new() -> Self { Self { system: system::Pallet::new(), balances: balances::Pallet::new() } } } fn main() { let mut runtime = Runtime::new(); let alice = "alice".to_string(); let bob = "bob".to_string(); let charlie = "charlie".to_string(); runtime.balances.set_balance(&alice, 100); // start emulating a block runtime.system.inc_block_number(); assert_eq!(runtime.system.block_number(), 1); // first transaction runtime.system.inc_nonce(&alice); let _res = runtime .balances .transfer(alice.clone(), bob, 30) .map_err(|e| eprintln!("{}", e)); // second transaction runtime.system.inc_nonce(&alice); let _res = runtime.balances.transfer(alice, charlie, 20).map_err(|e| eprintln!("{}", e)); /* TODO: Print the final runtime state after all transactions. */ }
#![allow(unused)] fn main() { use std::collections::BTreeMap; /// This is the System Pallet. /// It handles low level state needed for your blockchain. /* TODO: Add the derive macro to implement the `Debug` trait for `Pallet`. */ pub struct Pallet { /// The current block number. block_number: u32, /// A map from an account to their nonce. nonce: BTreeMap<String, u32>, } impl Pallet { /// Create a new instance of the System Pallet. pub fn new() -> Self { Self { block_number: 0, nonce: BTreeMap::new() } } /// Get the current block number. pub fn block_number(&self) -> u32 { self.block_number } // This function can be used to increment the block number. // Increases the block number by one. pub fn inc_block_number(&mut self) { self.block_number += 1; } // Increment the nonce of an account. This helps us keep track of how many transactions each // account has made. pub fn inc_nonce(&mut self, who: &String) { let nonce: u32 = *self.nonce.get(who).unwrap_or(&0); let new_nonce = nonce + 1; self.nonce.insert(who.clone(), new_nonce); } } #[cfg(test)] mod test { #[test] fn init_system() { let mut system = super::Pallet::new(); system.inc_block_number(); system.inc_nonce(&"alice".to_string()); assert_eq!(system.block_number(), 1); assert_eq!(system.nonce.get("alice"), Some(&1)); assert_eq!(system.nonce.get("bob"), None); } } }
#![allow(unused)] fn main() { use std::collections::BTreeMap; /// This is the Balances Module. /// It is a simple module which keeps track of how much balance each account has in this state /// machine. #[derive(Debug)] pub struct Pallet { // A simple storage mapping from accounts (`String`) to their balances (`u128`). balances: BTreeMap<String, u128>, } impl Pallet { /// Create a new instance of the balances module. pub fn new() -> Self { Self { balances: BTreeMap::new() } } /// Set the balance of an account `who` to some `amount`. pub fn set_balance(&mut self, who: &String, amount: u128) { self.balances.insert(who.clone(), amount); } /// Get the balance of an account `who`. /// If the account has no stored balance, we return zero. pub fn balance(&self, who: &String) -> u128 { *self.balances.get(who).unwrap_or(&0) } /// Transfer `amount` from one account to another. /// This function verifies that `from` has at least `amount` balance to transfer, /// and that no mathematical overflows occur. pub fn transfer( &mut self, caller: String, to: String, amount: u128, ) -> Result<(), &'static str> { let caller_balance = self.balance(&caller); let to_balance = self.balance(&to); let new_caller_balance = caller_balance.checked_sub(amount).ok_or("Not enough funds.")?; let new_to_balance = to_balance.checked_add(amount).ok_or("Overflow")?; self.balances.insert(caller, new_caller_balance); self.balances.insert(to, new_to_balance); Ok(()) } } #[cfg(test)] mod tests { #[test] fn init_balances() { let mut balances = super::Pallet::new(); assert_eq!(balances.balance(&"alice".to_string()), 0); balances.set_balance(&"alice".to_string(), 100); assert_eq!(balances.balance(&"alice".to_string()), 100); assert_eq!(balances.balance(&"bob".to_string()), 0); } #[test] fn transfer_balance() { let mut balances = super::Pallet::new(); assert_eq!( balances.transfer("alice".to_string(), "bob".to_string(), 51), Err("Not enough funds.") ); balances.set_balance(&"alice".to_string(), 100); assert_eq!(balances.transfer("alice".to_string(), "bob".to_string(), 51), Ok(())); assert_eq!(balances.balance(&"alice".to_string()), 49); assert_eq!(balances.balance(&"bob".to_string()), 51); assert_eq!( balances.transfer("alice".to_string(), "bob".to_string(), 51), Err("Not enough funds.") ); } } }
mod balances; mod system; // This is our main Runtime. // It accumulates all of the different pallets we want to use. #[derive(Debug)] pub struct Runtime { system: system::Pallet, balances: balances::Pallet, } impl Runtime { // Create a new instance of the main Runtime, by creating a new instance of each pallet. fn new() -> Self { Self { system: system::Pallet::new(), balances: balances::Pallet::new() } } } fn main() { let mut runtime = Runtime::new(); let alice = "alice".to_string(); let bob = "bob".to_string(); let charlie = "charlie".to_string(); runtime.balances.set_balance(&alice, 100); // start emulating a block runtime.system.inc_block_number(); assert_eq!(runtime.system.block_number(), 1); // first transaction runtime.system.inc_nonce(&alice); let _res = runtime .balances .transfer(alice.clone(), bob, 30) .map_err(|e| eprintln!("{}", e)); // second transaction runtime.system.inc_nonce(&alice); let _res = runtime.balances.transfer(alice, charlie, 20).map_err(|e| eprintln!("{}", e)); println!("{:#?}", runtime); }
#![allow(unused)] fn main() { use std::collections::BTreeMap; /// This is the System Pallet. /// It handles low level state needed for your blockchain. #[derive(Debug)] pub struct Pallet { /// The current block number. block_number: u32, /// A map from an account to their nonce. nonce: BTreeMap<String, u32>, } impl Pallet { /// Create a new instance of the System Pallet. pub fn new() -> Self { Self { block_number: 0, nonce: BTreeMap::new() } } /// Get the current block number. pub fn block_number(&self) -> u32 { self.block_number } // This function can be used to increment the block number. // Increases the block number by one. pub fn inc_block_number(&mut self) { self.block_number += 1; } // Increment the nonce of an account. This helps us keep track of how many transactions each // account has made. pub fn inc_nonce(&mut self, who: &String) { let nonce: u32 = *self.nonce.get(who).unwrap_or(&0); let new_nonce = nonce + 1; self.nonce.insert(who.clone(), new_nonce); } } #[cfg(test)] mod test { #[test] fn init_system() { let mut system = super::Pallet::new(); system.inc_block_number(); system.inc_nonce(&"alice".to_string()); assert_eq!(system.block_number(), 1); assert_eq!(system.nonce.get("alice"), Some(&1)); assert_eq!(system.nonce.get("bob"), None); } } }
diff --git a/src/balances.rs b/src/balances.rs
index 78f233df..1b9d9d85 100644
--- a/src/balances.rs
+++ b/src/balances.rs
@@ -3,6 +3,7 @@ use std::collections::BTreeMap;
/// This is the Balances Module.
/// It is a simple module which keeps track of how much balance each account has in this state
/// machine.
+/* TODO: Add the derive macro to implement the `Debug` trait for `Pallet`. */
pub struct Pallet {
// A simple storage mapping from accounts (`String`) to their balances (`u128`).
balances: BTreeMap<String, u128>,
diff --git a/src/main.rs b/src/main.rs
index 808d6732..b79655ae 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,6 +3,7 @@ mod system;
// This is our main Runtime.
// It accumulates all of the different pallets we want to use.
+/* TODO: Add the derive macro to implement the `Debug` trait for `Runtime`. */
pub struct Runtime {
system: system::Pallet,
balances: balances::Pallet,
@@ -37,4 +38,6 @@ fn main() {
// second transaction
runtime.system.inc_nonce(&alice);
let _res = runtime.balances.transfer(alice, charlie, 20).map_err(|e| eprintln!("{}", e));
+
+ /* TODO: Print the final runtime state after all transactions. */
}
diff --git a/src/system.rs b/src/system.rs
index 967578d0..cbf3b195 100644
--- a/src/system.rs
+++ b/src/system.rs
@@ -2,6 +2,7 @@ use std::collections::BTreeMap;
/// This is the System Pallet.
/// It handles low level state needed for your blockchain.
+/* TODO: Add the derive macro to implement the `Debug` trait for `Pallet`. */
pub struct Pallet {
/// The current block number.
block_number: u32,
diff --git a/src/balances.rs b/src/balances.rs
index 1b9d9d85..e09906cb 100644
--- a/src/balances.rs
+++ b/src/balances.rs
@@ -3,7 +3,7 @@ use std::collections::BTreeMap;
/// This is the Balances Module.
/// It is a simple module which keeps track of how much balance each account has in this state
/// machine.
-/* TODO: Add the derive macro to implement the `Debug` trait for `Pallet`. */
+#[derive(Debug)]
pub struct Pallet {
// A simple storage mapping from accounts (`String`) to their balances (`u128`).
balances: BTreeMap<String, u128>,
diff --git a/src/main.rs b/src/main.rs
index b79655ae..1ebef924 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -3,7 +3,7 @@ mod system;
// This is our main Runtime.
// It accumulates all of the different pallets we want to use.
-/* TODO: Add the derive macro to implement the `Debug` trait for `Runtime`. */
+#[derive(Debug)]
pub struct Runtime {
system: system::Pallet,
balances: balances::Pallet,
@@ -39,5 +39,5 @@ fn main() {
runtime.system.inc_nonce(&alice);
let _res = runtime.balances.transfer(alice, charlie, 20).map_err(|e| eprintln!("{}", e));
- /* TODO: Print the final runtime state after all transactions. */
+ println!("{:#?}", runtime);
}
diff --git a/src/system.rs b/src/system.rs
index cbf3b195..2fc5d764 100644
--- a/src/system.rs
+++ b/src/system.rs
@@ -2,7 +2,7 @@ use std::collections::BTreeMap;
/// This is the System Pallet.
/// It handles low level state needed for your blockchain.
-/* TODO: Add the derive macro to implement the `Debug` trait for `Pallet`. */
+#[derive(Debug)]
pub struct Pallet {
/// The current block number.
block_number: u32,