Thanks Jens - I appreciate the detailed response. The funny thing was
that I checked only the C++/Go generators - and stopped there (my
bad!) I'll have to spend some time thinking about what makes the most
sense in Rust.

Cheers,
Allen

Terminal Musings: http://www.allengeorge.com/
Raft in Java: https://github.com/allengeorge/libraft/
Twitter: https://twitter.com/allenageorge/


On Sun, Nov 20, 2016 at 6:32 AM, Jens Geyer <jensge...@hotmail.com> wrote:
> Hi Allen,
>
> there are three kinds of requiredness with Thrift:
>
> a) required
> The field must be written and the reader must be able to read it. If it is
> not properly set on write or if the reader does not find it in the data,
> some language implementations (not all) throw an exception.
>
> b) optional
> The field can be written, but may be as well missing form the data stream.
> Usually the implementations check on write whether the field is null or if
> the matching "isset" flag is set. What method is used is mostly an
> implememntation detail of the particular language. For convenience, some
> implementations use property setters to set the "isset" flag along with the
> value.
>
> c) default
> This is the default (hence the name) that is applied when neither required
> nor optional are specified. These fields are intended to be written always
> to the output stream. But, on the other hand, if the foo member in the Bar
> struct below is null, foo will in many (all?) cases not be written at all:
>
> struct Foo { ... }
> struct Bar {
>     1: Foo foo
> }
>
>
>> With the OPT_IN_REQ_OUT behavior it seems I have to serialize an empty
>> string over the wire. As a result, when the echo server responds, I
>> actually get:
>>
>> Foo { thing: Some("") }
>>
>> This seems very puzzling, because
>> the receiver can't tell the difference between sender-unset values and
>> values with the standard default.
>
> That may just be right from the formal definition, but there's a "but". I
> think the main confusion stems from the fact, that None is not a real value,
> but the attempt of getting out of the language what would be null values
> otherwise. The formally correct way would be to enforce a set field on write
> or throw otherwise, just like with "required". If we do a short facts check
> against the current code base, we find this behaviour for unset foo members:
>
> - C++ writes an empty foo
> - Python does not write unset foo
> - NodeJS does not write unset foo
> - Java  does not write unset foo
> - Delphi does not write unset foo
> - C_glib expects foo to be set and writes it
> - C# does not write unset foo
>
> So a lot of implementations treat "default" very similar to "optional", but
> some don't and follow the formal definition to always write some data. It
> works though, because on read the field is mandatory and the implementation
> provides some default for it, which in a lot of cases will again be "null".
>
> From my perspective, I would recommend to do the thing that makes most sense
> w/regard to the Rust language environment, the option that provides the
> least surprise.
>
> Have fun,
> JensG
>
>
> -----Ursprüngliche Nachricht-----
> From: Allen George
> Sent: Sunday, November 20, 2016 4:55 AM
> To: dev@thrift.apache.org
> Subject: Question about OPT_IN_REQ_OUT fields
>
> Hi,
>
> I've made a lot of progress on the Rust thrift generation front and
> I've been able to get the C++ server and Rust client communicating for
> some types. One thing really puzzles me however: T_OPT_IN_REQ_OUT
> fields. Looking at the docs and other code generators it looks like
> those fields are written out no matter what. Does that mean that I'm
> writing a default value (empty string, empty collections, 0 for
> numeric types) when they're not set? This seems very puzzling, because
> the receiver can't tell the difference between sender-unset values and
> values with the standard default. It's unfortunate from my point of
> view because a Rust client communicating with an echo server receives
> a result that doesn't match what it sent out!
>
> An example:
>
> struct Foo {
>   1: string thing;
> }
>
> Rust representation:
>
> struct Foo {
>   thing: Option<String>,
> }
>
> If I don't set "thing" I have the following value to send:
>
> let sent = Foo { thing: None }
>
> With the OPT_IN_REQ_OUT behavior it seems I have to serialize an empty
> string over the wire. As a result, when the echo server responds, I
> actually get:
>
> Foo { thing: Some("") }
>
> This means that my simple "check that the response is exactly what I
> sent out" doesn't quite work and I'll have to modify it for those
> cases where I have unset fields. At any rate - just wanted to check
> that my understanding of OPT_IN_REQ_OUT was right.
>
> Thanks,
> Allen
>
> Terminal Musings: http://www.allengeorge.com/
> Raft in Java: https://github.com/allengeorge/libraft/
> Twitter: https://twitter.com/allenageorge/
>

Reply via email to