DEV Community

Discussion on: Getting started with Kafka and Rust: Part 1

Collapse
niilz profile image
niilz • Edited

An idea about the impl ToBytes for User problem:

The issue is that in to_bytes we allocate a Vec<u8> on the heap and then want to return a pointer to the underlying byte-array. But the Vec goes out of scope when to_bytes ends. This cleans up the Vec and the pointer would be invalid if would be allowed to use it after the method returns.
Save but sad.

One option would be to build a wrapper around the User that contains an inner: User and an extra field bytes, that contains a Vec of u8.

Or, which is what I will show here, we could store the Vec directly on the User. While the field is excluded from serialization. Like so:

#[derive(Serialize, Deserialize, Debug)]
struct User {
    id: u32,
    email: String,
    #[serde(skip)]
    bytes: Option<Vec<u8>>,
}
Enter fullscreen mode Exit fullscreen mode

Then there can be an extra method that initializes the bytes:

impl User {
    fn calc_bytes(&mut self) {
        let serde_vec = serde_json::to_vec_pretty(self)
            .expect("json serialization failed");
        self.bytes = Some(serde_vec);
    }
}
Enter fullscreen mode Exit fullscreen mode

Which means, that as long as we initialize the bytes field before we call to_bytes the compiler allows us to return a pointer to the underlying bytes-array. Because the compiler knows that this pointer is valid as long as the Vec is valid, which in turn will not be dropped until the User instance goes our of scope.
The impl of ToBytes could look like this:

impl ToBytes for User {
    fn to_bytes(&self) -> &[u8] {
        match self.bytes {
            Some(ref bytes) => bytes,
            None => &[],
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

The full example can be seen at this playground-link:
play.rust-lang.org/?version=stable...