Rust中如何使用Borsh反序列化长度为1000000的自定义数组?
首先可以确定:Borsh支持对该类型的大数组进行序列化/反序列化,你遇到的报错是Borsh默认实现限制和存储设计不合理共同导致的,具体原因和解决方法如下:
报错根因
Borsh 0.10版本之前仅为长度≤32的数组默认实现了BorshSerialize、BorshDeserialize trait,你使用的长度为1000000的数组超出了默认实现范围,因此会抛出trait未实现的错误。
额外需要注意的是:(Pubkey, u8)单个元素大小为33字节,100万元素总大小为33MB,远超出Solana单账户最大10MB的存储上限,即便解决了序列化问题也无法部署上链。
可选解决方案
方案1:手动实现大数组的Borsh trait(仅适合本地测试)
如果你仅需要本地测试该结构,可以通过newtype包裹大数组后手动实现序列化/反序列化逻辑,示例代码如下:use borsh::{BorshSerialize, BorshDeserialize}; use anchor_lang::prelude::*; // 单个像素结构,派生Borsh trait #[derive(Clone, Copy, BorshSerialize, BorshDeserialize, Default)] pub struct Pixel(pub Pubkey, pub u8); // 大数组newtype包裹 #[derive(Clone, Copy, Default)] pub struct PixelArray(pub [Pixel; 1_000 * 1_000]); // 手动实现序列化 impl BorshSerialize for PixelArray { fn serialize<W: std::io::Write>(&self, writer: &mut W) -> std::io::Result<()> { for pixel in self.0.iter() { pixel.serialize(writer)?; } Ok(()) } } // 手动实现反序列化 impl BorshDeserialize for PixelArray { fn deserialize_reader<R: std::io::Read>(reader: &mut R) -> std::io::Result<Self> { let mut arr = [Pixel::default(); 1_000 * 1_000]; for pixel in arr.iter_mut() { *pixel = Pixel::deserialize_reader(reader)?; } Ok(Self(arr)) } }之后账户结构体中直接使用
pixels: PixelArray即可通过编译。方案2:拆分存储到动态Vec+多账户(生产环境推荐)
放弃单账户存储全量像素的设计,按坐标区块拆分像素存储到多个独立账户,每个账户内使用Borsh原生支持的Vec<(Pubkey, u8)>存储像素集合,单账户存储像素数量控制在30万以内即可满足10MB的大小限制。
对应账户字段定义如下:#[account] pub struct PixelChunk { // 区块坐标,用于定位 pub chunk_x: u16, pub chunk_y: u16, // 该区块内的像素集合,Borsh原生支持序列化 pub pixels: Vec<(Pubkey, u8)>, }初始化账户时需要提前计算空间:
8 + 2 + 2 + 4 + 33 * 单区块像素数量。方案3:单像素对应PDA存储(存储成本最优)
如果你的业务场景中需要频繁读写单个像素,可以直接为每个像素派生独立PDA,PDA种子使用像素的x、y坐标即可,单个像素账户仅需要存储33字节数据,不需要维护数组结构,完全避免了大数组序列化的问题,读写成本也更低。
内容的提问来源于stack exchange,提问作者Foobar

