ink!中使用双AccountId为键Vec<u128>为值的HashMap存储报PackedLayout未实现错误
问题原因
ink! 存储中ink_storage::collections::Vec(存储型Vec)实现的是SpreadLayout trait,而ink_storage::collections::HashMap的Value要求必须实现PackedLayout trait,两者不匹配触发该报错。存储型Vec是分散存储在链上存储槽的,不能被打包成连续块作为HashMap的值存储。
解决方案
方案1:替换为普通Vec(推荐,适用于数据量较小的场景)
把HashMap的Value类型换成ink! 标准库提供的普通Vec(来自ink_prelude),普通Vec实现了PackedLayout,可以直接作为HashMap的值使用。
修改后的存储代码如下:
// 首先导入依赖 use ink_prelude::vec::Vec; #[ink(storage)] pub struct Item { shipment: ink_storage::collections::HashMap< (AccountId, AccountId), Vec<u128>, >, }
初始化代码无需修改:
#[ink(constructor)] pub fn new() -> Self { Self { shipment: ink_storage::collections::HashMap::new(), } }
注意:该方案适合单个Vec的元素数量不超过数千的场景,如果元素过多会导致单次读写的gas费过高,甚至超出区块gas限制
方案2:使用Mapping适配大容量存储场景(适用于数据量大的场景)
如果你需要存储的Vec元素数量很大,更推荐用ink! 官方推荐的Mapping类型替代HashMap,通过嵌套索引的方式模拟存储型Vec的效果,读写单个元素的gas费稳定,不会随元素数量增长:
use ink_storage::Mapping; #[ink(storage)] pub struct Item { // 存储账户对对应Vec的总长度 shipment_len: Mapping<(AccountId, AccountId), u32>, // 通过 账户对+索引 存具体的u128值 shipment_items: Mapping<(AccountId, AccountId, u32), u128>, }
初始化代码:
#[ink(constructor)] pub fn new() -> Self { Self { shipment_len: Mapping::default(), shipment_items: Mapping::default(), } }
后续可自行封装push、get、len等方法实现Vec的常用操作即可。
补充:如果是ink! 4.x及以上版本,官方已经弃用ink_storage::collections::HashMap,统一使用Mapping即可,性能更好也不存在上述trait适配问题
内容的提问来源于stack exchange,提问作者user15205786
相关产品推荐
相关产品推荐

