Generating FIDL Rust crates
A FIDL Rust crate can be generated from a FIDL library in two ways:
- Manually, using the standard FIDL toolchain.
- Automatically, using the Fuchsia build system (which under the hood uses the standard FIDL toolchain). This option is only available within the Fuchsia source tree.
Libraries
A FIDL library
maps to a Rust library crate named fidl_
, followed by the
full library path with underscores instead of dots.
For example, given the library
declaration:
library fuchsia.examples;
The corresponding FIDL crate is named fidl_fuchsia_examples
:
use fidl_fuchsia_examples as fex;
Traits
Some methods and constants in FIDL Rust crates are provided by implementing traits from the FIDL runtime crate. To access them, you must import the corresponding traits. The easiest way to do this is to import them all at once from the prelude module:
use fidl::prelude::*;
The prelude re-exports traits using the as _
syntax, so the glob import only
brings traits into scope for resolving methods and constants. It does not import
the trait names themselves.
Constants
Given the constants:
const BOARD_SIZE uint8 = 9;
const NAME string = "Tic-Tac-Toe";
The FIDL toolchain generates the following constants:
pub const BOARD_SIZE: u8
pub const NAME: &str
The correspondence between FIDL primitive types and Rust types is outlined in built-in types.
Fields
This section describes how the FIDL toolchain converts FIDL types to native types in Rust. These types can appear as members in an aggregate type or as parameters to a protocol method.
Built-in types
In following table, when both an "owned" and "borrowed" variant are specified,
the "owned" type refers to the type that would appear in an aggregate type (e.g.
as the type of a struct field or vector element), and the "borrowed" type refers
to the type that would appear if it were used as a protocol method parameter
(from the client's perspective) or response tuple value (from the server's
perspective). The distinction between owned and borrowed exists in order to take
advantage of Rust’s ownership model. When making a request with a parameter of
type T
, the proxied function call does not need to take
ownership of T
so the FIDL toolchain needs to generate a borrowed version of
T
. Borrowed versions often use &mut
since the type T
may contain handles,
in which case the FIDL bindings zero out the handles when encoding, which
modifies the input. Using &mut
instead of taking ownership allows callers to
reuse the input value if it does not contain handles.
FIDL Type | Rust Type |
---|---|
bool |
bool |
int8 |
i8 |
int16 |
i16 |
int32 |
i32 |
int64 |
i64 |
uint8 |
u8 |
uint16 |
u16 |
uint32 |
u32 |
uint64 |
u64 |
float32 |
f32 |
float64 |
f64 |
array<T, N> |
&mut [T; N] (borrowed)[T, N] (owned) |
vector<T>:N |
&[T] (borrowed, when T is a numeric primitive)&mut dyn ExactSizeIterator (borrowed)Vec (owned) |
string |
&str (borrowed)String (owned) |
server_end:P |
fidl::endpoints::ServerEnd<PMarker> , where PMarker is the marker type for this protocol. |
client_end:P |
fidl::endpoints::ClientEnd<PMarker> where PMarker is the marker type for this protocol. |
zx.Handle |
fidl::Handle |
zx.Handle:S |
The corresponding handle type is used. For example,fidl::Channel or fidl::Vmo |
User defined types
Bits, enums, and tables are always referred to using their generated type T
.
structs and unions can be either required or optional, and used in an owned
context or borrowed context, which means that there are four possible equivalent
Rust types. For a given struct T
or union T
, the types are as follows:
owned | borrowed | |
---|---|---|
required | T |
&mut T |
optional | Option<T> |
Option<&mut T> |
Request, response, and event parameters
When FIDL needs to generate a single Rust type representing the parameters to a request, response, or event, such as for result types, it uses the following rules:
- Multiple parameters are represented as a tuple of the parameter types.
- A single parameter is represented just using the parameter's type.
- An empty set of parameters is represented using the unit type
()
.
Types
Bits
Given the bits definition:
type FileMode = strict bits : uint16 {
READ = 0b001;
WRITE = 0b010;
EXECUTE = 0b100;
};
The FIDL toolchain generates a set of
bitflags
called
FileMode
with flags FileMode::READ
, FileMode::WRITE
, and
FileMode::EXECUTE
.
The bitflags
struct also provides the following methods:
get_unknown_bits(&self) -> u16
: Returns a primitive value containing only the unknown members from this bits value. For strict bits, it is marked#[deprecated]
and always returns 0.has_unknown_bits(&self) -> bool
: Returns whether this value contains any unknown bits. For strict bits, it is marked#[deprecated]
and always returnsfalse
.
The generated FileMode
struct always has the complete set of #[derive]
rules.
Example usage:
let flags = fex::FileMode::READ | fex::FileMode::WRITE;
println!("{:?}", flags);
Enums
Given the enum definition:
type LocationType = strict enum {
MUSEUM = 1;
AIRPORT = 2;
RESTAURANT = 3;
};
The FIDL toolchain generates a Rust enum
using the specified underlying type,
or u32
if none is specified:
#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[repr(u32)]
pub enum LocationType {
Museum = 1,
Airport = 2,
Restaurant = 3,
}
With the following methods:
from_primitive(prim: u32) -> Option<Self>
: ReturnsSome
of the enum variant corresponding to the discriminant value if any, andNone
otherwise.into_primitive(&self) -> u32
: Returns the underlying discriminant value.is_unknown(&self) -> bool
: Returns whether this enum is unknown. For strict types, it is marked#[deprecated]
and always returnsfalse
.
If LocationType
is flexible, it will have the following
additional methods:
from_primitive_allow_unknown(prim: u32) -> Self
: Create an instance of the enum from a primitive value.unknown() -> Self
: Return a placeholder unknown enum value. If the enum contains a member marked with[Unknown]
, then the value returned by this method contains the value of specified unknown member.
The generated LocationType
enum
always has the complete set of #[derive]
rules.
Example usage:
let from_raw = fex::LocationType::from_primitive(1).expect("Could not create LocationType");
assert_eq!(from_raw, fex::LocationType::Museum);
assert_eq!(fex::LocationType::Restaurant.into_primitive(), 3);
To provide source-compatibility, flexible enums have an unknown
macro that should be used to match against unknown members instead of the _
pattern. For example, see the use of the LocationTypeUnknown!()
macro:
match location_type {
fex::LocationType::Museum => println!("museum"),
fex::LocationType::Airport => println!("airport"),
fex::LocationType::Restaurant => println!("restaurant"),
fex::LocationTypeUnknown!() => {
println!("unknown value: {}", location_type.into_primitive())
}
}
The unknown macro acts the same as a _
pattern, but it can be configured to
expand to an exhaustive match. This is useful for discovering missing cases.
Structs
Given the struct declaration:
type Color = struct {
id uint32;
@allow_deprecated_struct_defaults
name string:MAX_STRING_LENGTH = "red";
};
The FIDL toolchain generates a Rust struct
:
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct Color {
pub id: u32,
pub name: String,
}
The generated Color
struct
follows the #[derive]
rules.
Example usage:
let red = fex::Color { id: 0u32, name: "red".to_string() };
println!("{:?}", red);
Unions
Given the union definition:
type JsonValue = strict union {
1: int_value int32;
2: string_value string:MAX_STRING_LENGTH;
};
The FIDL toolchain generates a Rust enum
:
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub enum JsonValue {
IntValue(i32),
StringValue(String),
}
With the following methods:
ordinal(&self) -> u64
: Returns the ordinal of the active variant, as specified in the FIDL type.is_unknown(&self) -> bool
: Returns whether this union is unknown. Always returnsfalse
for non-flexible union types. For strict types, it is marked#[deprecated]
and always returnsfalse
.
If JsonValue
is flexible, it will have the following
additional methods:
unknown_variant_for_testing() -> Self
: Create an unknown union value. This should only be used in tests.
The generated JsonValue
enum
follows the #[derive]
rules.
Example usage:
let int_val = fex::JsonValue::IntValue(1);
let str_val = fex::JsonValue::StringValue("1".to_string());
println!("{:?}", int_val);
assert_ne!(int_val, str_val);
Flexible unions and unknown variants
Flexible unions have an extra variant generated to represent the unknown case. This variant is considered private and should not be referenced directly.
To provide source-compatibility, flexible unions have an
unknown macro that should be used to match against unknown members instead of
the _
pattern. For example, see the use of the JsonValueUnknown!()
macro:
match json_value {
fex::JsonValue::IntValue(val) => println!("int: {}", val),
fex::JsonValue::StringValue(val) => println!("string: {}", val),
fex::JsonValueUnknown!() => println!("unknown ordinal: {}", json_value.ordinal()),
}
The unknown macro acts the same as a _
pattern, but it can be configured to
expand to an exhaustive match. This is useful for discovering missing cases.
When a FIDL message containing a union with an unknown variant is decoded into
JsonValue
, JsonValue::is_unknown()
returns true.
Flexible unions have a custom PartialEq
to make unknown variants behave like
NaN: they do not compare equal to anything, including themselves. See RFC-0137:
Discard unknown data in FIDL for background on this decision.
Strict unions fail when decoding an unknown variant. Flexible unions succeed when decoding an unknown variant, but fail when re-encoding it.
Tables
Given the table definition:
type User = table {
1: age uint8;
2: name string:MAX_STRING_LENGTH;
};
The FIDL toolchain generates a struct
User
with optional members:
#[derive(Debug, PartialEq)]
pub struct User {
pub age: Option<u8>,
pub name: Option<String>,
#[doc(hidden)]
pub __source_breaking: fidl::marker::SourceBreaking,
}
If any unknown fields are encountered during decoding, they are discarded. There is no way to access them or determine if they occurred.
The __source_breaking
member signals that initializing the table exhaustively
causes API breakage when new fields are added. Instead, you should use the
struct update syntax to fill in unspecified fields with Default::default()
.
For example:
let user = fex::User { age: Some(20), ..Default::default() };
println!("{:?}", user);
assert!(user.age.is_some());
Similarly, tables do not permit exhaustive matching. Instead, you must use the
..
syntax to ignore unspecified fields. For example:
let fex::User { age, name, .. } = user;
The generated User
struct
follows the #[derive]
rules.
Inline layouts
The generated Rust code uses the the name reserved by fidlc
for
inline layouts.
Derives
When the FIDL toolchain generates a new struct
or enum
for a FIDL type, it
attempts to derive
as many traits from a predefined list of useful traits as
it can, including Debug
, Copy
, Clone
, etc. The complete list of traits can
be found in Appendix A.
For aggregate types, such as structs, unions, and tables, the set of derives is
determined by starting with the list of all possible derives and then removing
some based on the fields that are transitively present in the type. For example,
aggregate types that transitively contain a vector
do not derive Copy
, and
types that my contain a handle
(i.e. types that are not marked as
resource
) do not derive Copy
and Clone
. When in doubt,
refer to the generated code to check which traits are derived by a specific
type. See Appendix B for implementation details.
Protocols
Given a protocol:
closed protocol TicTacToe {
strict StartGame(struct {
start_first bool;
});
strict MakeMove(struct {
row uint8;
col uint8;
}) -> (struct {
success bool;
new_state box<GameState>;
});
strict -> OnOpponentMove(struct {
new_state GameState;
});
};
The main entrypoint for interacting with TicTacToe
is the TicTacToeMarker
struct, which contains two associated types:
Proxy
: The associated proxy type for use with async clients. In this example, this is a generatedTicTacToeProxy
type. Synchronous clients should useTicTacToeSynchronousProxy
directly (see Synchronous), which is not stored in an associated type on theTicTacToeMarker
.RequestStream
: The associated request stream that servers implementing this protocol will need to handle. In this example, this isTicTacToeRequestStream
, which is generated by FIDL.
Additionally, TicTacToeMarker
has the following associated constants from
implementing fidl::endpoints::ProtocolMarker
:
DEBUG_NAME: &’static str
: The name of the service suitable for debug purposes.
Other code may be generated depending on the Protocol and method attributes applied to the protocol or its methods.
Client
Asynchronous
For asynchronous clients, the FIDL toolchain generates a TicTacToeProxy
struct
with the following:
Associated types:
TicTacToeProxy::MakeMoveResponseFut
: TheFuture
type for the response of a two way method. This type implementsstd::future::Future<Output = Result<(bool, Option<Box<GameState>>), fidl::Error>> + Send
.
Methods:
new(channel: fidl::AsyncChannel) -> TicTacToeProxy
: Create a new proxy forTicTacToe
.take_event_stream(&self) -> TicTacToeEventStream
: Get aStream
of events from the server end (see Events).
Methods from implementing fidl::endpoints::Proxy
:
from_channel(channel: fidl::AsyncChannel) -> TicTacToeProxy
: Same asTicTacToeProxy::new
.into_channel(self) -> Result<fidl::AsyncChannel>
: Attempt to convert the proxy back into a channel.as_channel(&self) -> &fidl::AsyncChannel
: Get a reference to the proxy's underlying channelis_closed(&self) -> bool
: Check if the proxy has received thePEER_CLOSED
signal.on_closed<'a>(&'a self) -> fuchsia_async::OnSignals<'a>
: Get a future that completes when the proxy receives thePEER_CLOSED
signal.
Methods from implementing TicTacToeProxyInterface
:
start_game(&self, mut start_first: bool) -> Result<(), fidl::Error>
: Proxy method for a fire and forget protocol method. It takes as arguments the request parameters and returns an empty result.make_move(&self, mut row: u8, mut col: u8) -> Self::MakeMoveResponseFut
: Proxy method for a two way method. It takes as arguments the request parameters and returns aFuture
of the response.
An example of setting up an asynchronous proxy is available in the Rust tutorial.
The TicTacToeProxyInterface
trait can be useful for testing client code. For
example, if you write a function that takes &T
as a parameter where T:
TicTacToeProxyInterface
, you can unit test it with a fake proxy type:
use futures::future::{ready, Ready};
struct FakeTicTacToeProxy {
move_response: (bool, Option<Box<GameState>>),
}
impl TicTacToeProxyInterface for FakeTicTacToeProxy {
fn start_game(&self, mut start_first: bool) -> Result<(), fidl::Error> {
Ok(())
}
type MakeMoveResponseFut = Ready<fidl::Result<(bool, Option<Box<GameState>>)>>;
fn make_move(&self, mut row: u8, mut col: u8) -> Self::MakeMoveResponseFut {
ready(self.move_response.clone())
}
}
Synchronous
For synchronous clients of the TicTacToe
protocol, the FIDL toolchain
generates a TicTacToeSynchronousProxy
struct with the following methods:
new(channel: fidl::Channel) -> TicTacToeSynchronousProxy
: Returns a new synchronous proxy over the client end of a channel. The server end is assumed to implement theTicTacToe
protocol.into_channel(self) -> fidl::Channel
: Convert the proxy back into a channel.start_game(&self, mut a: i64) -> Result<(), fidl::Error>
: Proxy method for a fire and forget method: it takes the request parameters as arguments and returns an empty result.make_move(&self, mut row: u8, mut col: u8, __deadline: zx::MonotonicInstant) -> Result<(bool, Option<Box<GameState>>), fidl::Error>
: Proxy method for a two way method. It takes the request parameters as arguments followed by a deadline parameter, which dictates how long the method call will wait for a response (orzx::MonotonicInstant::INFINITE
to block indefinitely). It returns aResult
of the response parameters.wait_for_event(&self, deadline: zx::MonotonicInstant) -> Result<TicTacToeEvent, fidl::Error>
: Blocks until an event is received or the deadline expires (usezx::MonotonicInstant::INFINITE
to block indefinitely). It returns aResult
of theTicTacToeEvent
enum.
An example of setting up a synchronous proxy is available in the Rust tutorial.
Server
Protocol request stream
To represent the stream of incoming requests to a server, the FIDL toolchain
generates a TicTacToeRequestStream
type that implements futures::Stream<Item
= Result<TicTacToeRequest, fidl::Error>>
as well as
fidl::endpoints::RequestStream
. Each protocol has a corresponding request
stream type.
Request enum
TicTacToeRequest
is an enum representing the possible requests of the
TicTacToe
protocol. It has the following variants:
StartGame { start_first: bool, control_handle: TicTacToeControlHandle }
: A fire and forget request, which contains the request parameters and a control handle.MakeMove { row: u8, col: u8, responder: TicTacToeMakeMoveResponder }
: A two way method request, which contains the request parameters and a responder.
One such enum is generated for each protocol.
Request responder
Each two way method has a corresponding generated responder type, which the
server uses to respond to a request. In this example, which only has one two way
method, the FIDL toolchain generates TicTacToeMakeMoveResponder
, which
provides the following methods:
send(self, mut success: bool, mut new_state: Option<&mut GameState>) -> Result<(), fidl::Error>
: Sends a response.send_no_shutdown_on_err(self, mut success: bool, mut new_state: Option<&mut GameState>) -> Result<(), fidl::Error>
: Similar tosend
but does not shut down the channel if an error occurs.control_handle(&self) -> &TicTacToeControlHandle
: Get the underlying control handle.drop_without_shutdown(mut self)
: Drop the Responder without shutting down the channel.
Protocol control handle
The FIDL toolchain generates TicTacToeControlHandle
to encapsulate the client
endpoint of the TicTacToe
protocol on the server side. It contains the
following methods:
shutdown(&self)
: Shut down the channel.shutdown_with_epitaph(&self, status: zx_status::Status)
: Send an epitaph and then shut down the channel.send_on_opponent_move(&self, mut new_state: &mut GameState) -> Result<(), fidl::Error>
: Proxy method for an event, which takes as arguments the event’s parameters and returns an empty result (see Events).
Events
Client
For receiving events on the asynchronous client, the FIDL toolchain generates a
TicTacToeEventStream
, which can be obtained using the take_event_stream()
method on the TicTacToeProxy
.
TicTacToeEventStream
implements futures::Stream<Item = Result<TicTacToeEvent,
fidl::Error>>
.
For receiving events on the synchronous client, the FIDL toolchain generates a
wait_for_event
method on the
TicTacToeSynchronousProxy
that returns a
TicTacToeEvent
.
TicTacToeEvent
is an enum representing the possible events. It has the
following variants:
OnOpponentMove { new_state: GameState }
: Discriminant for theTicTacToeEvent
event.
And provides the following methods:
into_on_opponent_move(self) -> Option<GameState>
: ReturnSome
of the parameters of the event, orNone
if the variant does not match the method call.
Server
Servers can send events by using the control handle
corresponding to the protocol. The control handle can be obtained through a
TicTacToeRequest
received from the client. For fire and forget methods, the
control handle is available through the control_handle
field, and for two way
methods, it is available through the control_handle()
method on the responder.
A control handle for a protocol can also be obtained through the corresponding
request stream (in this example, TicTacToeRequestStream
), since it implements
fidl::endpoints::RequestStream
.
Results
For a method with an error type:
protocol TicTacToe {
MakeMove(struct {
row uint8;
col uint8;
}) -> (struct {
new_state GameState;
}) error MoveError;
};
The FIDL toolchain generates a public TicTacToeMakeMoveResult
type alias for
std::result::Result<GameState, MoveError>
. The rest of the bindings code for
this method is generated as if it has a single response parameter result
of
type TicTacToeMakeMoveResult
. The type used for a successful result follows
the parameter type conversion rules.
Unknown interaction handling
Server-side
When a protocol is declared as open
or ajar
, the generated request
enum will will have an additional variant called
_UnknownMethod
which has these fields:
#[non_exhausitve]
_UnknownMethod {
/// Ordinal of the method that was called.
ordinal: u64,
/// Control handle for the protocol.
control_handle: TicTacToeControlHandle,
/// Enum indicating whether the method is a one-way method or a two way
/// method. This field only exists if the protocol is open.
method_type: fidl::MethodType,
}
MethodType
is an enum with two unit variants, OneWay
and TwoWay
,
which tells which kind of method was called.
Whenever the server receives a flexible unknown event, the request stream will emit this variant of the request enum.
Client-side
There is no way for the client to tell if a flexible
one-way method was known
to the server or not. For flexible
two-way methods, if the method is not known
to the server, the client will receive an Err
result with a value of
fidl::Error::UnsupportedMethod
. The UnsupportedMethod
error is only possible
for a flexible two-way method.
Aside from the possibility of getting an UnsupportedMethod
error, there are no
API differences between strict
and flexible
methods on the client.
For open
and ajar
protocols, the generated event
enum will have an additional variant called
_UnknownEvent
which has these fields:
#[non_exhaustive]
_UnknownEvent {
/// Ordinal of the event that was sent.
ordinal: u64,
}
Whenever the client receives an unknown event, the client event stream will emit this variant of the event enum.
Protocol composition
FIDL does not have a concept of inheritance, and generates full code as described above for all composed protocols. In other words, the code generated for the following:
protocol A {
Foo();
};
protocol B {
compose A;
Bar();
};
Is the same as the following code:
protocol A {
Foo();
};
protocol B {
Foo();
Bar();
};
Protocol and method attributes
Transitional
The @transitional
attribute only affects the ProxyInterface
trait, which is
sometimes used in test code. For non-test code, protocols can be transitioned on
the server side by having request handlers temporarily use a catch-all match arm
in the Request
handler. Client code does not need to be soft transitioned
since the generated proxy will always implement all methods.
For methods annotated with the @transitional
attribute, the ProxyInterface
trait for asynchronous clients provides
default implementations that call unimplemented!()
. As noted earlier, this has
no effect on the Proxy
type, which always implements all the trait's methods.
However, it can help for soft transitions when the ProxyInterface
trait is
used for fake proxies in client-side unit tests.
Discoverable
For protocols annotated with the @discoverable
attribute, the Marker type
additionally implements the fidl::endpoints::DiscoverableProtocolMarker
trait.
This provides the PROTOCOL_NAME
associated constant.
Explicit encoding and decoding
FIDL messages are automatically encoded when they are sent and decoded when they are received. You can also encode and decode explicitly, for example to persist FIDL data to a file. Following RFC-0120: Standalone use of the FIDL wire format, the bindings offer a persistence API and a standalone API.
Persistence
The recommended way to to explicitly encode and decode is to use persist
and
unpersist
. This works for non-resource structs, tables, and
unions.
For example, you can persist a Color
struct:
let original_value = fex::Color { id: 0, name: "red".to_string() };
let bytes = fidl::persist(&original_value)?;
And then unpersist it later:
let decoded_value = fidl::unpersist(&bytes)?;
assert_eq!(original_value, decoded_value);
Standalone
For advanced use cases where the persistence API is not sufficient, you can use the standalone encoding and decoding API. This works for all structs, tables, and unions. There are two sets of functions: one for value types, and one for resource types.
For example, since JsonValue
union is a value type, we encode
it using standalone_encode_value
:
let original_value = fex::JsonValue::StringValue("hello".to_string());
let (bytes, wire_metadata) = fidl::standalone_encode_value(&original_value)?;
This returns a vector of bytes and an opaque object that stores wire format
metadata. To decode, pass both values to standalone_decode_value
:
let decoded_value = fidl::standalone_decode_value(&bytes, &wire_metadata)?;
assert_eq!(original_value, decoded_value);
As another example, consider the struct EventStruct
:
type EventStruct = resource struct {
event zx.Handle:<EVENT, optional>;
};
Since this is a resource type, we encode it using
standalone_encode_resource
:
let original_value = fex::EventStruct { event: Some(fidl::Event::create()) };
let (bytes, handle_dispositions, wire_metadata) =
fidl::standalone_encode_resource(original_value)?;
In addition to bytes and wire format metadata, this also returns a vector of
HandleDisposition
s. To decode, convert the handle dispositions to
HandleInfo
s, and then pass all three values to
standalone_decode_resource
:
let mut handle_infos = fidl::convert_handle_dispositions_to_infos(handle_dispositions)?;
let decoded_value: fex::EventStruct =
fidl::standalone_decode_resource(&bytes, &mut handle_infos, &wire_metadata)?;
assert!(decoded_value.event.is_some());
Appendix A: Derived traits
"Debug",
"Copy",
"Clone",
"Default",
"Eq",
"PartialEq",
"Ord",
"PartialOrd",
"Hash",
Appendix B: Fill derives
The calculation of traits derivation rules is visible in fidlgen_rust:
// Calculates what traits should be derived for each output type,
// filling in all `*derives` in the IR.
func (c *compiler) fillDerives(ir *Root) {