LOADING...
LOADING...
LOADING...
当前位置: 玩币族首页 > 新闻观点 > 【Substrate开发教程】21 - Substrate Runtime中使用Struct结构体

【Substrate开发教程】21 - Substrate Runtime中使用Struct结构体

2020-11-05 松果 来源:区块链网络


在Rust中,Struct(结构体)是一种自定义的数据结构,可以对其命名和将多个有意义的关联数据打包成一组。

Rust中的Struct类似面向对象语言(Java、C++)中的类(class),不过Rust中的Struct只包含对象的属性,不包含其方法。

编写模块代码

修改pallets/template/src/lib.rs的代码如下:

#![cfg_attr(not(feature = "std"), no_std)] use frame_support::{ codec::{Decode, Encode}, decl_event, decl_module, decl_storage, dispatch::DispatchResult, }; use frame_system::{self as system, ensure_signed}; use sp_runtime::RuntimeDebug; pub trait Trait: balances::Trait + system::Trait { type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>; } #[derive(Encode, Decode, Clone, Default, RuntimeDebug)] pub struct InnerThing<Hash, Balance> { number: u32, hash: Hash, balance: Balance, } type InnerThingOf<T> = InnerThing<<T as system::Trait>::Hash, <T as balances::Trait>::Balance>; #[derive(Encode, Decode, Default, RuntimeDebug)] pub struct SuperThing<Hash, Balance> { super_number: u32, inner_thing: InnerThing<Hash, Balance>, } decl_storage! { trait Store for Module<T: Trait> as NestedStructs { InnerThingsByNumbers get(fn inner_things_by_numbers): map hasher(blake2_128_concat) u32 => InnerThingOf<T>; SuperThingsBySuperNumbers get(fn super_things_by_super_numbers): map hasher(blake2_128_concat) u32 => SuperThing<T::Hash, T::Balance>; } } decl_event!( pub enum Event<T> where <T as system::Trait>::Hash, <T as balances::Trait>::Balance { NewInnerThing(u32, Hash, Balance), NewSuperThingByExistingInner(u32, u32, Hash, Balance), NewSuperThingByNewInner(u32, u32, Hash, Balance), } ); decl_module! { pub struct Module<T: Trait> for enum Call where origin: T::Origin { fn deposit_event() = default; #[weight = 10_000] fn insert_inner_thing(origin, number: u32, hash: T::Hash, balance: T::Balance) -> DispatchResult { let _ = ensure_signed(origin)?; let thing = InnerThing {number, hash, balance}; <InnerThingsByNumbers<T>>::insert(number, thing); Self::deposit_event(RawEvent::NewInnerThing(number, hash, balance)); Ok(()) } #[weight = 10_000] fn insert_super_thing_with_existing_inner(origin, inner_number: u32, super_number: u32) -> DispatchResult { let _ = ensure_signed(origin)?; let inner_thing = Self::inner_things_by_numbers(inner_number); let super_thing = SuperThing { super_number, inner_thing: inner_thing.clone(), }; <SuperThingsBySuperNumbers<T>>::insert(super_number, super_thing); Self::deposit_event(RawEvent::NewSuperThingByExistingInner(super_number, inner_thing.number, inner_thing.hash, inner_thing.balance)); Ok(()) } #[weight = 10_000] fn insert_super_thing_with_new_inner(origin, inner_number: u32, hash: T::Hash, balance: T::Balance, super_number: u32) -> DispatchResult { let _ = ensure_signed(origin)?; let inner_thing = InnerThing { number: inner_number, hash, balance, }; <InnerThingsByNumbers<T>>::insert(inner_number, inner_thing.clone()); Self::deposit_event(RawEvent::NewInnerThing(inner_number, hash, balance)); let super_thing = SuperThing { super_number, inner_thing, }; <SuperThingsBySuperNumbers<T>>::insert(super_number, super_thing); Self::deposit_event(RawEvent::NewSuperThingByNewInner(super_number, inner_number, hash, balance)); Ok(()) } } }

下面详细分析模块代码。

配置Cargo.toml

使用了sp_runtime和balances模块,需要在Cargo.toml中进行配置。

[dependencies]下添加:

sp-runtime = { version = '2.0.0', default-features = false } balances = { package = 'pallet-balances', version = '2.0.0', default-features = false }

[features]的std数组下添加:

'balances/std', 'sp-runtime/std',

定义一个Struct

为Substrate Runtime定义一个简单的结构体,使用如下语法:

#[derive(Encode, Decode, Default, Clone, PartialEq)] pub struct MyStruct { some_number: u32, optional_number: Option<u32>, }

上面的代码片段声明了derive(派生)宏,以确保MyStruct的指定特征(Encode、Decode、Default、Clone、PartialEq)符合共享行为。

如果希望将此struct存储在区块链上,则需要派生或手动implement这些特征。

为了使用Encode和Decode特征,需要先导入:

use frame_support::codec::{Encode, Decode};

自定义#[derive]宏

自定义#[derive]宏在结构体和枚举上指定通过derive属性添加代码,是一种过程宏。

编译器能够通过#[derive]属性为某些traits提供基本实现,如果需要更复杂的行为,仍可以手动实现这些traits。

以下是可派生(derivable)特征(Traits)的列表:

比较特征:Eq、PartialEq、Ord、PartialOrd;Copy,用类型的“复制语义”代替“移动语义”;Clone,通过副本从&T创建T;Hash,从&T计算哈希值;Default,创建一个数据类型的空实例;Debug,使用{:?}格式化一个值;

具有pallet通用字段的Struct

上面定义的Struct仅使用了Rust的基本数据类型,通常情况下如果想存储来自pallet的配置特征,则必须在Struct的定义中使用通用类型参数。

代码如下:

#[derive(Encode, Decode, Clone, Default, RuntimeDebug)] pub struct InnerThing<Hash, Balance> { number: u32, hash: Hash, balance: Balance, }

可以看到,这里在Struct中使用了Hash和Balance类型,这两个类型是来自system和balances模块的配置traits,在声明结构体时需要将它们指定成范型。

通常以pallet的配置特征T作为单个参数类型别名,这样可以节省将来的输入:

type InnerThingOf<T> = InnerThing<<T as system::Trait>::Hash, <T as balances::Trait>::Balance>;

使用Struct作为存储对象

使用Struct作为存储对象时与使用基本类型时没有明显区别,使用泛型结构时需要提供所有泛型类型参数。

decl_storage! { trait Store for Module<T: Trait> as NestedStructs { InnerThingsByNumbers get(fn inner_things_by_numbers): map hasher(blake2_128_concat) u32 => InnerThingOf<T>; SuperThingsBySuperNumbers get(fn super_things_by_super_numbers): map hasher(blake2_256) u32 => SuperThing<T::Hash, T::Balance>; } }

与存储映射进行交互的方式与不使用任何自定义结构时完全相同:

fn insert_inner_thing(origin, number: u32, hash: T::Hash, balance: T::Balance) -> DispatchResult { let _ = ensure_signed(origin)?; let thing = InnerThing {number, hash, balance}; <InnerThingsByNumbers<T>>::insert(number, thing); Self::deposit_event(RawEvent::NewInnerThing(number, hash, balance)); Ok(()) }

嵌套Struct

结构体可以包含其他结构体做为其字段,内部结构体所需的任何泛型类型也必须提供给外部:

#[derive(Encode, Decode, Default, RuntimeDebug)] pub struct SuperThing<Hash, Balance> { super_number: u32, inner_thing: InnerThing<Hash, Balance>, }

编译

编译命令如下:

cd substrate-node-template cargo +nightly-2020-08-23 build --release

使用了nightly-2020-08-23这个较稳定的cargo nightly版本以避免编译过程中出现bug。


测试

启动node-template

./target/release/node-template --dev

打开https://polkadot.js.org/apps,切换网络为DEVELOPMENT-Local Node:


选项卡选择开发者-交易,“提交下面的外部信息”选择templateModule,会自动获取到定义的函数:

insertInnerThing(number, hash, balance)insertSuperThingWithExistingInner(inner_number, super_number)insertSuperThingWithNewInner(inner_number, hash, balance, super_number)


这里选择调用insertInnerThing函数,设定参数number为20,hash为默认值,balance为1000Unit。

点击右下角“提交交易”按钮,点击“签名并提交”:


就会调用decl_module!中定义的insert_inner_thing函数,并触发事件NewInnerThing。

选项卡选择网络-浏览-链信息,在右侧可以看到最新触发的事件:


可以看到触发的事件是模块代码中定义的templateModule.NewInnerThing。

—-

编译者/作者:松果

玩币族申明:玩币族作为开放的资讯翻译/分享平台,所提供的所有资讯仅代表作者个人观点,与玩币族平台立场无关,且不构成任何投资理财建议。文章版权归原作者所有。

LOADING...
LOADING...