2

I am attempting to save the shared reference to a single memory location with Serde. I have a small struct container that contains two attributes of type Arc<RwLock<f64>>. My goal is for both the attributes to share a single memory location so that when I change one, the other also changes. This works when container is created in main, but when I load in the serialized struct ref0 and ref1 no longer share the same memory location, and contain unique memory locations with the same value.

Is it possible to indicate to Serde that ref0 and ref1 are supposed to share a memory location and be "linked?"

#[derive(Serialize, Deserialize)]
struct Container {
    pub(crate) ref0: Arc<RwLock<f64>>,
    pub(crate) ref1: Arc<RwLock<f64>>
}

Saving Scenario (Works)

//makers ref0 and ref1, assign them to container.
let mut ref0 = Arc::new(RwLock::new(1.0));
let ref1 = ref0.clone();
let container = Container {
    ref0,
    ref1
};

// Checks that changing one ref will alter the other.
mem::replace(&mut *container.ref0.write().unwrap(), 2.0);
println!("{} {}",container.ref0.read().unwrap(), container.ref1.read().unwrap()); // should output "2.0 2.0" and does.
mem::replace(&mut *container.ref0.write().unwrap(), 3.0);
println!("{} {}",container.ref0.read().unwrap(), container.ref1.read().unwrap()); // should output "3.0 3.0" and does.

// Serializes the struct and save
let f_name = "SaveTest.test";
let mut file = File::create( f_name).unwrap();
let _result = file.write_all(&bincode::serialize(&container).unwrap());

Load Scenario (Does not Work)

//LOAD THE CONTAINER
let mut buffer : Vec<u8> = vec![];
BufReader::new(File::open("SaveTest.test").unwrap()).read_to_end(&mut buffer).unwrap();
let container : Container = bincode::deserialize(&buffer).unwrap();

//Verifies that each ref is the same value when it was saved
println!("{} {}",container.ref0.read().unwrap(), container.ref1.read().unwrap()); // Should output "3.0 3.0" and does
mem::replace(&mut *container.ref0.write().unwrap(), 4.0);
println!("{} {}",container.ref0.read().unwrap(), container.ref1.read().unwrap()); // Should output "4.0 4.0" but outputs "4.0 3.0"
Chayim Friedman
  • 47,971
  • 5
  • 48
  • 77
  • 1
    I don't think this is possible. From https://serde.rs/feature-flags.html#-features-rc: "Serializing and deserializing these types does not preserve identity and may result in multiple copies of the same data." – Chayim Friedman Dec 03 '21 at 10:38
  • 1
    In general serialization is not copying and does not preserve memory-related aspects of the values; it is designed to _bypass_ them. – Chayim Friedman Dec 03 '21 at 10:39

1 Answers1

1

In is not possible to serialize references with Serde. See see How do I use Serde to deserialize structs with references from a reader?

You could make a custom wrapper type that takes a deserialized Container value as f64, and builds a proper instance with Arc-s inside:

#[derive(Serialize, Deserialize)]
struct ContainerData {
    pub value: f64,
}

struct Container {
    pub(crate) ref0: Arc<RwLock<f64>>,
    pub(crate) ref1: Arc<RwLock<f64>>
}

impl Container {
    fn new(data: ContainerData) -> Self {
        let data_ref = Arc::new(data.value);
        Self {
            ref0: Arc::clone(&data_ref),
            ref1: Arc::clone(&data_ref),
        }
    }
}
battlmonstr
  • 5,841
  • 1
  • 23
  • 33
  • From what I understand from your comment, I need to make two structs that are designed to hold the same data. One struct is useful for storing the references in a meaningful way, and the other is useful for using those references. – Christian Potts Dec 03 '21 at 14:09
  • 1
    Yes, separate serialization code from the model processing code. – battlmonstr Dec 03 '21 at 14:47