Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

impl<'a> Serialize for fmt::Arguments<'a> #1319

Closed
dpc opened this issue Jun 21, 2018 · 3 comments
Closed

impl<'a> Serialize for fmt::Arguments<'a> #1319

dpc opened this issue Jun 21, 2018 · 3 comments

Comments

@dpc
Copy link
Contributor

dpc commented Jun 21, 2018

Would it make sense to impl<'a> Serialize for fmt::Arguments<'a> ?

It's a very handy type, and available in core library. Though I guess for best support, the Serializer would have to get a serialize_fmt method, so it is possible to write it out without allocating temporary strings, etc. That would probably be a breaking change, though maybe an implementation that serializes into temporary, and calls serialize_str or something would do.

@oli-obk
Copy link
Member

oli-obk commented Jun 26, 2018

Well... We could add an impl of std::fmt::Write for all Serializers. I guess the write_str method could just forward to serialize_str:

DISCLAIMER: all the code below is entirely untested and probably will not compile without some adjustments

impl<S: Serializer> std::fmt::Write for S {
    #[inline]
    fn write_str(&mut self, s: &str) -> std::fmt::Result {
        self.serialize_str(s).map_err(|_| std::fmt::Error)
    }
}

Although that would be a breaking change, because someone might have implemented Write for their serializer already.

We can still do this by adding a wrapper type for Serializers that we then use inside the Serialize impl of fmt::Arguments

impl<'a> Serialize for fmt::Arguments<'a> {
    fn serialize<S: Serializer>(&self, s: &mut S) -> Result<(), S::Error> {
        struct Wrapper<'b, S: Serializer>(&'b mut S, Option<S::Error>);
        impl<'b, S: Serializer> std::fmt::Write for Wrapper<'b, S> {
            #[inline]
            fn write_str(&mut self, s: &str) -> std::fmt::Result {
                self.0.serialize_str(s).map_err(|err| {
                    assert!(self.1.is_none());
                    self.1 = Some(err);
                    std::fmt::Error
                })
            }
        }
        let mut wrapper = Wrapper(s);
        match wrapper.write_fmt(*self) {
            Ok(()) => Ok(()),
            Err(std::fmt::Error) => Ok(wrapper.1.unwrap()),
        }
    }
}

Unfortunately this means you get a bunch of strings in the output, not sure how useful that is. What's the expected output format? A sequence of strings?

@dpc
Copy link
Contributor Author

dpc commented Jun 26, 2018

Thanks for response. I don't think serializaing this value to series of serialize_str is going to work, but I might be wrong.

What's the expected output format? A sequence of strings?

In my use-case (slog and logging in general) fmt::Arguments is just a string that wasn't yet "put together" (lazy-evalution-sort-of). When logging, it's useful to take &fmt::Arguments because it's fast, and only if a given logging statement/value reaches logging output make a string out of it/write it out to output. So from my perspective fmt::Arguments == lazily evaluated String.

I'm toying with some API re-designs where publicly accepted types would be &dyn serde::Serailize, and I'm loosing fmt::Arguments support because serde doesn't support it. I need to be able to do things like impl<T> Something for T where T : serde:Serialize, but without serde having impl Serialize for fmt::Arguments, this can't be done due to coherency conflicts. Well, I could just do impl for each single type that serde supports, but it's not perfect.

In ideal case Serializer would get a serialize_fmt that would be much like one call to serialize_str, but serializer would be responsible for either writing it directly to its output, or to temporary string. I am not sure if this is possible, especially without breaking backward compat. Maybe adding a method like that with default impl that writes it out to String and calls write_str would be OK for non-no_std, and in no_std it could just return error/do nothing.

@dpc
Copy link
Contributor Author

dpc commented Jun 27, 2018

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants