使用Serde序列化Arc<RwLock>时如何保留字段的内存共享关系
问题根源
默认Serde的Serialize/Deserialize派生实现对Arc<T>的处理是直接序列化T的内容,不会记录多个Arc指向同一块内存的关联关系。反序列化时每个Arc<RwLock<f64>>字段都会独立创建新的内存实例,自然就失去了共享特性。
解决方案
方案1:优化结构体设计(最推荐,无额外开销)
如果两个字段本质就是同一个值的共享引用,完全可以只存储一份基础的Arc,对外提供访问方法返回克隆即可,完全兼容原有使用逻辑:
use serde::{Serialize, Deserialize}; use std::sync::{Arc, RwLock}; #[derive(Serialize, Deserialize)] struct Container { inner: Arc<RwLock<f64>>, } impl Container { // 对外提供和原有结构兼容的访问接口 pub(crate) fn ref0(&self) -> Arc<RwLock<f64>> { self.inner.clone() } pub(crate) fn ref1(&self) -> Arc<RwLock<f64>> { self.inner.clone() } // 如果需要直接赋值也可以提供对应的setter方法 pub(crate) fn set_val(&self, new_val: f64) { *self.inner.write().unwrap() = new_val; } }
序列化时只会存储一份值,反序列化后两个访问方法返回的都是同一个Arc的克隆,天然保持内存共享。
方案2:保留原有结构体字段,手动实现序列化/反序列化逻辑
如果必须保留ref0、ref1两个公开字段的结构,可以手动实现两个Serde trait,序列化时仅存一份值,反序列化时直接克隆Arc赋值给两个字段:
use serde::{Serialize, Serializer, Deserialize, Deserializer}; use std::sync::{Arc, RwLock}; struct Container { pub(crate) ref0: Arc<RwLock<f64>>, pub(crate) ref1: Arc<RwLock<f64>> } impl Serialize for Container { fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { // 只需要序列化一次共享的值即可 self.ref0.read().unwrap().serialize(serializer) } } impl<'de> Deserialize<'de> for Container { fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> where D: Deserializer<'de>, { let val = f64::deserialize(deserializer)?; let base_arc = Arc::new(RwLock::new(val)); Ok(Self { ref0: base_arc.clone(), ref1: base_arc, }) } }
如果你的场景存在跨多个结构体的共享Arc引用,需要全局追踪引用关系,可以使用serde_with库的Arc序列化扩展开启引用计数追踪能力,单结构体内部共享的场景用上述两个方案足够,无需引入额外依赖。
内容的提问来源于stack exchange,提问作者Christian Potts
相关产品推荐
相关产品推荐

