PSA: FIDL now supports table and union payloads

20 views
Skip to first unread message

Alex Zaslavsky

unread,
Mar 28, 2022, 12:51:25 PM3/28/22
to fidl...@fuchsia.dev, anno...@fuchsia.dev

If you don't read/write FIDL, or interact with generated FIDL bindings, you may stop reading now.

TL;DR:

  • FIDL now supports using unions and tables as top-level method payloads:

// Old way

protocol OldProtocol {

    MyMethod(struct { inner table {...}; });

};


// New way - not 100% equivalent in bindings output, see below!

protocol NewProtocol {

    MyMethod(table {...});

};


  • This style is encouraged for new method definitions where appropriate, but already existing definitions will not be proactively converted to support this new syntax.

The Fine Print

Today, a payload message that is a struct consisting of a single table and/or union is a fairly common pattern in FIDL living in fuchsia.git.  Removing this unnecessary layer of indirection makes APIs like these easier to use, easier to evolve, and more directly expressive of their author's intent.

There are some differences in how the bindings will render table or union payloads.  Unlike structs, which have all of their members "flattened" in most bindings, signatures for methods containing table or union payloads always have a single payload argument representing the entire table/union.  Consider the following FIDL:

protocol Example {

    UsingStruct(struct {

        a bool;

        b uint16;

        c string;

    });

    // Don't do this anymore...

    UsingTableOld(struct {

        in table {

            1: a bool;

            2: b uint16;

            3: c string;

        };

    });

    // ...do this instead!

    UsingTable(table {

        1: a bool;

        2: b uint16;

        3: c string;

    });

};

Which produces this HLCPP output:

class Example {

 public:

  /* … */

  virtual void UsingStruct(bool a, uint16_t b, std::string c) = 0;

  // Note that the function below is unflattened, with 1 argument, instead of 3.

  virtual void UsingTableNew(fidl::mylib::ExampleUsingTableNewRequest payload) = 0;

  // Tables generated in the old manner will have different arg names/types.

  virtual void UsingTableOld(fidl::mylib::ExampleUsingTableOldRequestIn in) = 0;

};

And this Rust output:

pub trait ExampleProxyInterface: Send + Sync {

  fn r#using_struct(&self, a: bool, b: u16, c: &str) -> Result<(), fidl::Error>;

  // Note that the function below is unflattened, with 2 arguments, instead of 4.

  fn r#using_table_new(&self, payload: ExampleUsingTableNewRequest) -> Result<(), fidl::Error>;

  // Tables generated in the old manner will have different arg names/types.

  fn r#using_table_old(&self, in_: ExampleUsingTableOldRequestIn) -> Result<(), fidl::Error>;

}

The new one argument configuration for tables and unions makes method signatures easier to evolve: when a new field is added to the the request payload of UsingTable, none of the generated function signatures change, ensuring API compatibility.  However, because UsingTable and UsingTableOld will produce different names in the resulting bindings, care must be taken to avoid API breakage when converting to the new syntax.

What Do I Need To Do?

The API rubric will be updated to recommend using top-level unions and tables in lieu of their struct-wrapped alternatives, but there is no need to proactively update existing usages of that pattern.

Are There Examples For How To Use This In My Favorite Language?

The FIDL compatibility tests have several new test cases, with corresponding implementations in each of our supported binding flavors (Dart, Go, HLCPP, LLCPP, Rust).  These should provide example patterns for the most common usages of top-level union/table payloads.

Pascal

unread,
Mar 28, 2022, 1:29:17 PM3/28/22
to Bruno Dal Bo, announce, Alex Zaslavsky, fidl...@fuchsia.dev
I'm assuming that migrating from the table-in-struct way to inline-table is wire compatible but source breaking, is that correct? Or did the envelope struct affect the wire format in any way?

That's correct: ABI compatible, but API breaking.

Alex Zaslavsky

unread,
Mar 28, 2022, 1:30:12 PM3/28/22
to Bruno Dal Bo, announce, fidl...@fuchsia.dev
That is correct: such a conversion would be source-breaking (in the manner highlighted in the original announcement post), but ABI compatible.

On Mon, Mar 28, 2022 at 10:11 AM Bruno Dal Bo <bruno...@google.com> wrote:
Pardon if this is a silly question,

I'm assuming that migrating from the table-in-struct way to inline-table is wire compatible but source breaking, is that correct? Or did the envelope struct affect the wire format in any way?

Bruno Dal Bo

unread,
Mar 28, 2022, 1:31:43 PM3/28/22
to announce, Alex Zaslavsky, fidl...@fuchsia.dev
Pardon if this is a silly question,

I'm assuming that migrating from the table-in-struct way to inline-table is wire compatible but source breaking, is that correct? Or did the envelope struct affect the wire format in any way?

On Monday, March 28, 2022 at 9:52:24 AM UTC-7 Alex Zaslavsky wrote:

Shai Barack

unread,
Mar 28, 2022, 3:52:07 PM3/28/22
to Alex Zaslavsky, Bruno Dal Bo, announce, fidl...@fuchsia.dev
Appreciate you making this very common pattern easier on the eyes. Thanks for thinking of compatibility and evolution. 

--
All posts must follow the Fuchsia Code of Conduct https://fuchsia.dev/fuchsia-src/CODE_OF_CONDUCT or may be removed.
---
You received this message because you are subscribed to the Google Groups "announce" group.
To unsubscribe from this group and stop receiving emails from it, send an email to announce+u...@fuchsia.dev.
To view this discussion on the web visit https://groups.google.com/a/fuchsia.dev/d/msgid/announce/CAPkkwny0VWP_ve6TyKG_UdO48AKa9RK3YiG5FVCBoe-Z3U-o5A%40mail.gmail.com.
Reply all
Reply to author
Forward
0 new messages