Rust中Serde反序列化时缓存Transaction引发递归错误的解决方案咨询
你遇到的这个递归问题其实很好理解——当你为Transaction手动实现Deserialize trait时,在方法里调用serde::de::Deserialize::deserialize(deserializer),这个调用实际上会再次触发你自己写的Transaction::deserialize方法,形成了无限递归循环,编译器和运行时的报错都是这个循环导致的。
要打破这个循环,核心思路是避免在自己的deserialize实现里直接调用针对Transaction的默认反序列化,而是借助一个中间类型来完成默认反序列化,再转换成Transaction并完成缓存。下面是具体的实现方案:
解决方案:使用中间结构体承接默认反序列化
我们可以定义一个和Transaction结构完全一致的内部结构体(比如叫TransactionDef),让它自动派生Deserialize trait,这样它的反序列化逻辑是Serde自动生成的,不会和我们手动实现的Transaction逻辑冲突。然后在Transaction的deserialize方法里,先把数据反序列化成这个中间结构体,再转换成Transaction,最后执行缓存操作。
示例代码如下:
use serde::{Serialize, Deserialize}; use uuid::Uuid; #[derive(Serialize, Debug)] pub struct Transaction { pub(crate) id: Uuid, // 这里放你的其他字段,比如金额、时间等 } // 定义内部结构体,和Transaction的字段完全一致,自动派生Deserialize #[derive(Deserialize)] struct TransactionDef { id: Uuid, // 复制Transaction的所有其他字段,确保结构完全匹配 } impl<'de> Deserialize<'de> for Transaction { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: serde::de::Deserializer<'de> { // 先反序列化成中间结构体,这里用的是Serde自动生成的默认逻辑,不会递归 let trans_def = TransactionDef::deserialize(deserializer)?; // 将中间结构体转换为Transaction let transaction = Transaction { id: trans_def.id, // 对应赋值其他字段 }; // 在这里执行你的缓存操作,比如存入HashMap(注意线程安全问题,如果是多线程场景要加锁) // 假设你有一个全局缓存,比如: // CACHE.lock().unwrap().insert(transaction.id, transaction.clone()); Ok(transaction) } }
为什么你参考的其他代码没有这个问题?
你之前看到的示例代码,大概率是为包装类型或者其他和当前类型不同的结构体实现Deserialize,比如为Box<Transaction>或者某个自定义的Wrapper实现反序列化,这时调用默认反序列化是针对内部的Transaction,而不是自己的实现,所以不会触发递归。而你是直接为Transaction本身实现,所以直接调用默认反序列化就会递归到自己的方法。
额外注意事项
关于缓存的实现,如果你用全局变量,要注意线程安全问题,推荐用std::sync::Mutex或者tokio::sync::Mutex(如果是异步场景)来包裹你的缓存容器(比如HashMap<Uuid, Transaction>)。另外,如果你需要在反序列化过程中传递缓存(而不是用全局变量),可以研究Serde的DeserializeSeed trait,它允许你在反序列化时携带额外的上下文数据。
备注:内容来源于stack exchange,提问作者Saish

