Serialize concatenated `str`s

I have a struct S { a: &'static str, b: &'static str }
I want to serialize it as a + b, so S { a: "aaa", b: "bbb" } should be serialized as "aaabbb"

how do I achieve this without allocations?

Yes, well mostly:

you'd utilize the collect_str method which avoids allocation of an intermediate String for those serializers that can support that, while those that don't would fall back to creating an allocation.

could you provide an example? collect_str still expects just one value that is Display

Probably easiest to use format_args, e.g.

use serde::{Serialize, Serializer};

struct S {
    a: &'static str,
    b: &'static str,
}

impl Serialize for S {
    fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
    where
        Ser: Serializer,
    {
        serializer.collect_str(&format_args!("{a}{b}", a = self.a, b = self.b))
    }
}

fn main() {
    let s = S { a: "aaa", b: "bbb" };

    match serde_json::to_string(&s) {
        Ok(json) => println!("Serialized: {}", json),
        Err(e) => println!("Error during serialization: {}", e),
    }
}

Rust Playground

I'm not immediately sure if writing a manual Display implementation has performance benefits over this though; the potential to avoid allocations is the same either way though. Edit: Yes, there's probably some very minor overhead in using format_args because of an extra layer of dynamic calls to get to the bottom of the write_str calls. Assuming you're wanting to avoid the allocation because the strings might be large, if that's the case, then the overhead becomes negligible.

Using a manual Display implementation could look like this:

impl Serialize for S {
    fn serialize<Ser>(&self, serializer: Ser) -> Result<Ser::Ok, Ser::Error>
    where
        Ser: Serializer,
    {
        struct Wrap<'a>(&'a S);
        impl std::fmt::Display for Wrap<'_> {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
                f.write_str(self.0.a)?;
                f.write_str(self.0.b)
            }
        }
        serializer.collect_str(&Wrap(self))
    }
}
3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.