near / borsh-rs

Rust implementation of Binary Object Representation Serializer for Hashing
https://borsh.io/
Apache License 2.0
310 stars 67 forks source link

Unable to serialize after adding elements to array in struct #50

Closed dmr07 closed 2 years ago

dmr07 commented 2 years ago

Hi, I'm new learning to use Borsh and I'm trying to use it to serialize and deserialize arrays. I see on the borsh.io website under specifications that dynamic sized arrays are supported, but I'm having trouble getting it to work. Below I modified the example given on the webpage, and you can see the outputs of the program in the following code section.

Main results: I'm able to modify x, but when I try to add an element, no changes are reflected as seen in the second encoded_a output. I tried to deserialize the encoding again after serializing the changes, but it's giving me an error.

It would be great to know the correct way to serialize arrays after adding elements or alternative approaches. In any case, any help would be appreciated

use borsh::{BorshSerialize, BorshDeserialize};
use std::cell::{RefCell};

fn main() {
    #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
    struct A {
        x: u8,
        y: Vec<String>,
    }

    let a = A {
        x: 1,
        y: vec!["cat".to_string()],
    };

    let encoded_a = RefCell::new(a.try_to_vec().unwrap());
    println!("Before: {:?}", encoded_a);

    let mut decoded_a = A::try_from_slice(&encoded_a.borrow()).unwrap();

    println!("Decoded Before:  {:?}", decoded_a);
    decoded_a.x = 2;
    decoded_a.y.push("dog".to_string());

    decoded_a.serialize(&mut &mut encoded_a.borrow_mut()[..]);

    println!("After:  {:?}", encoded_a);
    let mut decoded2_a = A::try_from_slice(&encoded_a.borrow());
    println!("Decoded After:  {:?}", decoded2_a);
}

Outputs:

Before: RefCell { value: [1, 1, 0, 0, 0, 3, 0, 0, 0, 99, 97, 116] }
Decoded Before:  A { x: 1, y: ["cat"] }
After:  RefCell { value: [2, 2, 0, 0, 0, 3, 0, 0, 0, 99, 97, 116] }
Decoded After:  Err(Custom { kind: InvalidInput, error: "Unexpected length of input" })
matklad commented 2 years ago

Compiling the code gives the following warning:

warning: unused `Result` that must be used
  --> src/main.rs:25:5
   |
25 |     decoded_a.serialize(&mut &mut encoded_a.borrow_mut()[..]);
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: `#[warn(unused_must_use)]` on by default
   = note: this `Result` may be an `Err` variant, which should be handled

If you handle that result, you'll see that serialization actually errors. That is because you are trying to serialize to &mut &mut [u8], rather than &mut Vec<u8>, which doesn't have enough space.

Here's a fixed example which works.

use borsh::{BorshDeserialize, BorshSerialize};

fn main() {
    #[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug)]
    struct A {
        x: u8,
        y: Vec<String>,
    }

    let a = A {
        x: 1,
        y: vec!["cat".to_string()],
    };

    let encoded_a = a.try_to_vec().unwrap();
    println!("Before: {:?}", encoded_a);

    let mut decoded_a = A::try_from_slice(&encoded_a).unwrap();

    println!("Decoded Before:  {:?}", decoded_a);
    decoded_a.x = 2;
    decoded_a.y.push("dog".to_string());

    let mut encoded_a = Vec::new();
    decoded_a.serialize(&mut encoded_a).unwrap();

    println!("After:  {:?}", encoded_a);
    let decoded2_a = A::try_from_slice(&encoded_a).unwrap();
    println!("Decoded After:  {:?}", decoded2_a);
}

Closing, as this is not a borsh bug, but rather a programming error which the compiler warns about.