Open didactic-drunk opened 5 years ago
I'd be happy to consider such a PR. This is very old stuff and things have changed a lot in the Crystal ecosystem.
I think keeping a method that returns the IO would be good though (like to_io
or something).
I 100% understand why #to_protobuf
was chosen as a name but I believe it's actually a misnomer. It's already a Protobuf. Conventions exist for converting objects to slices and strings with #to_slice
and #to_s(io)
.
I don't know if anyone's using this shard in production but, if so, it might make sense to add @Deprecated
to #to_protobuf
instead of changing or removing it.
#to_json
, #to_yaml
, #to_msgpack
. All of them return String
or Bytes
. I have multiple cases where I need to make more than one pass at the returned object. I can't use to_protobuf(IO)
#to_slice
wouldn't let you choose between different serialization formats.
Example:
# Public API uses JSON.
test = Test.from_json network_socket
# Internal API is passed and parsed multiple times using protobuf for speed.
test.to_protobuf message_bus
#to_json
,#to_yaml
,#to_msgpack
. All of them returnString
orBytes
.
The use case for those methods is to serialize arbitrary objects into those formats. The use case for protobufs is to serialize objects whose entire purpose for existing is the serialization format. A Protobuf message isn't a domain model.
The equivalent use case to those methods would be something like this:
# An example domain model
class Product
# This mixin would provide methods to deserialize and serialize the domain model
# via the specified `Protobuf::Message` type.
include Protobuf::Serializable(MyProtobufNamespace::Product)
end
Then you'd call Product#to_protobuf
and that would make perfect sense, but even then, the that leaves the question, should the return value there be a MyProtobufNamespace::Product
or the over-the-wire serialized bytes?
Converting between JSON and protobuf seems common enough. I don't see a problem sharing the same serialized class or struct. See #26 for an example.
We shouldn't encourage messages to share code with anything else because they're typically generated from .proto
files, not written directly in the target language.
Either way, there's no confusion about what Protobuf::Message#to_s(io)
and Protobuf::Message#to_slice
would do based on their names. They're more accurate and expressive than Protobuf::Message#to_protobuf
. I also think changing return values of existing methods to incompatible types should be done sparingly as not to break existing code.
I think I agree. @jeromegn thoughts?
My Crystal is very far away in my mind. I've mostly been writing Rust for over a year!
I will rely on the 2 of you. If you agree, sounds good enough for me. From reading the discussion, I think I agree too!
The default
#to_protobuf
method introduces subtle bugs.#rewind
on the returnedIO::Memory
is always necessary to make use of the value and it's easy to forget potentially causing data loss.When combining this with HMAC or compression IO's where you may pass over the data more than once you need to rewind each time otherwise you read 0 bytes. Each pass is a possible bug.
Most serializers
#to_*
methods returnString
orBytes
. It's also the safer alternative.Would a PR be accepted to change the default type to something less error prone and more in line with crystal norms?