Substrate 的 `fn deposit_event<T>() = default` 中的 `<T>` 有什么作用?
What's the function of `<T>` in Substrate's `fn deposit_event<T>() = default`?
关于 Substrate Collectables Workshop,<T>
在 fn deposit_event<T>() = default;
中实际做什么和指代什么?当我的 Event
不包括例如 AccountId
时,我可以省略它吗?
在 Substrate 的背景下,这里有几件事要回答。
通用类型
每个 Substrate 模块都能够定义模块所需的自定义类型,所有这些都是实现某些特征所必需的。与强定义这些类型不同,使用 traits 可以让我们确保类型具有某些属性,但不限于类型可以是什么。这些 generic 类型定义位于 trait Trait
中,必须在每个模块中定义。
这里是 system
模块中定义的自定义类型的示例,几乎所有其他模块都使用它:
pub trait Trait: 'static + Eq + Clone {
/// The aggregated `Origin` type used by dispatchable calls.
type Origin: ...
/// Account index (aka nonce) type. This stores the number of previous transactions associated with a sender
/// account.
type Index: ...
/// The block number type used by the runtime.
type BlockNumber: ...
/// The output of the `Hashing` function.
type Hash: ...
/// The hashing system (algorithm) being used in the runtime (e.g. Blake2).
type Hashing: Hash<Output = Self::Hash>;
/// Collection of (light-client-relevant) logs for a block to be included verbatim in the block header.
type Digest: ...
/// The user account identifier type for the runtime.
type AccountId: ...
/// Converting trait to take a source type and convert to `AccountId`.
///
/// Used to define the type and conversion mechanism for referencing accounts in transactions. It's perfectly
/// reasonable for this to be an identity conversion (with the source type being `AccountId`), but other modules
/// (e.g. Indices module) may provide more functional/efficient alternatives.
type Lookup: StaticLookup<Target = Self::AccountId>;
/// The block header.
type Header: ...
/// The aggregated event type of the runtime.
type Event: Parameter + Member + From<Event>;
/// A piece of information that can be part of the digest (as a digest item).
type Log: From<Log<Self>> + Into<DigestItemOf<Self>>;
}
在您的自定义模块中,您将定义如下内容:
/// The module's configuration trait.
pub trait Trait: system::Trait {
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}
注意这里我们已经定义了我们自己的 Event
类型,我们的模块现在可以使用它,而且我们还继承 system
模块 (system::Trait
),所以我们也可以使用所有这些类型!
使用通用类型
现在我们已经在运行时模块中定义了一些自定义类型,我们可以开始在整个模块逻辑中使用它们。
您将始终看到与此代码段中的第二行类似的一行:
decl_storage! {
trait Store for Module<T: Trait> as Sudo {
/// The `AccountId` of the sudo key.
Key get(key) config(): T::AccountId;
}
}
这一行有些是宏魔法,但重要的是你看到我们定义了 Module<T: Trait>
。这意味着我们已将模块的 trait Trait
分配为别名 T
下的 Module
的通用参数。
因此,我们可以像上面那样使用 T
来引用这些特殊类型:
T::AccountId
绕一圈,我们可以访问这个类型,因为我们从 system::Trait
定义中继承了它!
存款事件宏生成
为了最终回答您的问题,decl_module!
函数生成了 deposit_event
的函数体,以避免您一遍又一遍地编写相同的代码。
我们检测 pre-defined 函数名称 deposit_event
,并检查 = default;
,然后将其替换为有效的 deposit_event
函数。
但是,当生成这个函数时,宏不知道它是否应该使用输入事件是泛型的函数,或者输入事件不使用泛型。因此,您需要通过说 deposit_event<T>()
或 deposit_event()
.
来 "give it a hint"
然后它将生成适用于模块中类型的函数的正确版本。如果您的模块事件使用任何通用类型,例如 T::AccountId
或 T::Balance
,您还需要定义 deposit_event
.
的通用版本
关于 Substrate Collectables Workshop,<T>
在 fn deposit_event<T>() = default;
中实际做什么和指代什么?当我的 Event
不包括例如 AccountId
时,我可以省略它吗?
在 Substrate 的背景下,这里有几件事要回答。
通用类型
每个 Substrate 模块都能够定义模块所需的自定义类型,所有这些都是实现某些特征所必需的。与强定义这些类型不同,使用 traits 可以让我们确保类型具有某些属性,但不限于类型可以是什么。这些 generic 类型定义位于 trait Trait
中,必须在每个模块中定义。
这里是 system
模块中定义的自定义类型的示例,几乎所有其他模块都使用它:
pub trait Trait: 'static + Eq + Clone {
/// The aggregated `Origin` type used by dispatchable calls.
type Origin: ...
/// Account index (aka nonce) type. This stores the number of previous transactions associated with a sender
/// account.
type Index: ...
/// The block number type used by the runtime.
type BlockNumber: ...
/// The output of the `Hashing` function.
type Hash: ...
/// The hashing system (algorithm) being used in the runtime (e.g. Blake2).
type Hashing: Hash<Output = Self::Hash>;
/// Collection of (light-client-relevant) logs for a block to be included verbatim in the block header.
type Digest: ...
/// The user account identifier type for the runtime.
type AccountId: ...
/// Converting trait to take a source type and convert to `AccountId`.
///
/// Used to define the type and conversion mechanism for referencing accounts in transactions. It's perfectly
/// reasonable for this to be an identity conversion (with the source type being `AccountId`), but other modules
/// (e.g. Indices module) may provide more functional/efficient alternatives.
type Lookup: StaticLookup<Target = Self::AccountId>;
/// The block header.
type Header: ...
/// The aggregated event type of the runtime.
type Event: Parameter + Member + From<Event>;
/// A piece of information that can be part of the digest (as a digest item).
type Log: From<Log<Self>> + Into<DigestItemOf<Self>>;
}
在您的自定义模块中,您将定义如下内容:
/// The module's configuration trait.
pub trait Trait: system::Trait {
/// The overarching event type.
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
}
注意这里我们已经定义了我们自己的 Event
类型,我们的模块现在可以使用它,而且我们还继承 system
模块 (system::Trait
),所以我们也可以使用所有这些类型!
使用通用类型
现在我们已经在运行时模块中定义了一些自定义类型,我们可以开始在整个模块逻辑中使用它们。
您将始终看到与此代码段中的第二行类似的一行:
decl_storage! {
trait Store for Module<T: Trait> as Sudo {
/// The `AccountId` of the sudo key.
Key get(key) config(): T::AccountId;
}
}
这一行有些是宏魔法,但重要的是你看到我们定义了 Module<T: Trait>
。这意味着我们已将模块的 trait Trait
分配为别名 T
下的 Module
的通用参数。
因此,我们可以像上面那样使用 T
来引用这些特殊类型:
T::AccountId
绕一圈,我们可以访问这个类型,因为我们从 system::Trait
定义中继承了它!
存款事件宏生成
为了最终回答您的问题,decl_module!
函数生成了 deposit_event
的函数体,以避免您一遍又一遍地编写相同的代码。
我们检测 pre-defined 函数名称 deposit_event
,并检查 = default;
,然后将其替换为有效的 deposit_event
函数。
但是,当生成这个函数时,宏不知道它是否应该使用输入事件是泛型的函数,或者输入事件不使用泛型。因此,您需要通过说 deposit_event<T>()
或 deposit_event()
.
然后它将生成适用于模块中类型的函数的正确版本。如果您的模块事件使用任何通用类型,例如 T::AccountId
或 T::Balance
,您还需要定义 deposit_event
.