xref: /aosp_15_r20/external/flatbuffers/docs/source/RustUsage.md (revision 890232f25432b36107d06881e0a25aaa6b473652)
1Use in Rust    {#flatbuffers_guide_use_rust}
2==========
3
4## Before you get started
5
6Before diving into the FlatBuffers usage in Rust, it should be noted that
7the [Tutorial](@ref flatbuffers_guide_tutorial) page has a complete guide
8to general FlatBuffers usage in all of the supported languages (including Rust).
9This page is designed to cover the nuances of FlatBuffers usage, specific to
10Rust.
11
12#### Prerequisites
13
14This page assumes you have written a FlatBuffers schema and compiled it
15with the Schema Compiler. If you have not, please see
16[Using the schema compiler](@ref flatbuffers_guide_using_schema_compiler)
17and [Writing a schema](@ref flatbuffers_guide_writing_schema).
18
19Assuming you wrote a schema, say `mygame.fbs` (though the extension doesn't
20matter), you've generated a Rust file called `mygame_generated.rs` using the
21compiler (e.g. `flatc --rust mygame.fbs` or via helpers listed in "Useful
22tools created by others" section bellow), you can now start using this in
23your program by including the file. As noted, this header relies on the crate
24`flatbuffers`, which should be in your include `Cargo.toml`.
25
26## FlatBuffers Rust library code location
27
28The code for the FlatBuffers Rust library can be found at
29`flatbuffers/rust`. You can browse the library code on the
30[FlatBuffers GitHub page](https://github.com/google/flatbuffers/tree/master/rust).
31
32## Testing the FlatBuffers Rust library
33
34The code to test the Rust library can be found at `flatbuffers/tests/rust_usage_test`.
35The test code itself is located in
36[integration_test.rs](https://github.com/google/flatbuffers/blob/master/tests/rust_usage_test/tests/integration_test.rs)
37
38This test file requires `flatc` to be present. To review how to build the project,
39please read the [Building](@ref flatbuffers_guide_building) documentation.
40
41To run the tests, execute `RustTest.sh` from the `flatbuffers/tests` directory.
42For example, on [Linux](https://en.wikipedia.org/wiki/Linux), you would simply
43run: `cd tests && ./RustTest.sh`.
44
45*Note: The shell script requires [Rust](https://www.rust-lang.org) to
46be installed.*
47
48## Using the FlatBuffers Rust library
49
50*Note: See [Tutorial](@ref flatbuffers_guide_tutorial) for a more in-depth
51example of how to use FlatBuffers in Rust.*
52
53FlatBuffers supports both reading and writing FlatBuffers in Rust.
54
55To use FlatBuffers in your code, first generate the Rust modules from your
56schema with the `--rust` option to `flatc`. Then you can import both FlatBuffers
57and the generated code to read or write FlatBuffers.
58
59For example, here is how you would read a FlatBuffer binary file in Rust:
60First, include the library and generated code. Then read the file into
61a `u8` vector, which you pass, as a byte slice, to `root_as_monster()`.
62
63This full example program is available in the Rust test suite:
64[monster_example.rs](https://github.com/google/flatbuffers/blob/master/tests/rust_usage_test/bin/monster_example.rs)
65
66It can be run by `cd`ing to the `rust_usage_test` directory and executing: `cargo run monster_example`.
67
68~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.rs}
69    extern crate flatbuffers;
70
71    #[allow(dead_code, unused_imports)]
72    #[path = "../../monster_test_generated.rs"]
73    mod monster_test_generated;
74    pub use monster_test_generated::my_game;
75
76    use std::io::Read;
77
78    fn main() {
79        let mut f = std::fs::File::open("../monsterdata_test.mon").unwrap();
80        let mut buf = Vec::new();
81        f.read_to_end(&mut buf).expect("file reading failed");
82
83        let monster = my_game::example::root_as_monster(&buf[..]);
84~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
85
86`monster` is of type `Monster`, and points to somewhere *inside* your
87buffer (root object pointers are not the same as `buffer_pointer` !).
88If you look in your generated header, you'll see it has
89convenient accessors for all fields, e.g. `hp()`, `mana()`, etc:
90
91~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.rs}
92        println!("{}", monster.hp());     // `80`
93        println!("{}", monster.mana());   // default value of `150`
94        println!("{:?}", monster.name()); // Some("MyMonster")
95    }
96~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
97
98*Note: That we never stored a `mana` value, so it will return the default.*
99
100## Direct memory access
101
102As you can see from the above examples, all elements in a buffer are
103accessed through generated accessors. This is because everything is
104stored in little endian format on all platforms (the accessor
105performs a swap operation on big endian machines), and also because
106the layout of things is generally not known to the user.
107
108For structs, layout is deterministic and guaranteed to be the same
109across platforms (scalars are aligned to their
110own size, and structs themselves to their largest member), and you
111are allowed to access this memory directly by using `safe_slice`
112on the reference to a struct, or even an array of structs.
113
114To compute offsets to sub-elements of a struct, make sure they
115are structs themselves, as then you can use the pointers to
116figure out the offset without having to hardcode it. This is
117handy for use of arrays of structs with calls like `glVertexAttribPointer`
118in OpenGL or similar APIs.
119
120It is important to note is that structs are still little endian on all
121machines, so the functions to enable tricks like this are only exposed on little
122endian machines. If you also ship on big endian machines, using an
123`#[cfg(target_endian = "little")]` attribute would be wise or your code will not
124compile.
125
126The special function `safe_slice` is implemented on Vector objects that are
127represented in memory the same way as they are represented on the wire. This
128function is always available on vectors of struct, bool, u8, and i8. It is
129conditionally-compiled on little-endian systems for all the remaining scalar
130types.
131
132The FlatBufferBuilder function `create_vector_direct` is implemented for all
133types that are endian-safe to write with a `memcpy`. It is the write-equivalent
134of `safe_slice`.
135
136## Access of untrusted buffers
137
138The safe Rust functions to interpret a slice as a table (`root`,
139`size_prefixed_root`, `root_with_opts`, and `size_prefixed_root_with_opts`)
140verify the data first. This has some performance cost, but is intended to be
141safe for use on flatbuffers from untrusted sources. There are corresponding
142`unsafe` versions with names ending in `_unchecked` which skip this
143verification, and may access arbitrary memory.
144
145The generated accessor functions access fields over offsets, which is
146very quick. The current implementation uses these to access memory without any
147further bounds checking. All of the safe Rust APIs ensure the verifier is run
148over these flatbuffers before accessing them.
149
150When you're processing large amounts of data from a source you know (e.g.
151your own generated data on disk), the `_unchecked` versions are acceptable, but
152when reading data from the network that can potentially have been modified by an
153attacker, it is desirable to use the safe versions which use the verifier.
154
155## Threading
156
157Reading a FlatBuffer does not touch any memory outside the original buffer,
158and is entirely read-only (all immutable), so is safe to access from multiple
159threads even without synchronisation primitives.
160
161Creating a FlatBuffer is not thread safe. All state related to building
162a FlatBuffer is contained in a FlatBufferBuilder instance, and no memory
163outside of it is touched. To make this thread safe, either do not
164share instances of FlatBufferBuilder between threads (recommended), or
165manually wrap it in synchronisation primitives. There's no automatic way to
166accomplish this, by design, as we feel multithreaded construction
167of a single buffer will be rare, and synchronisation overhead would be costly.
168
169Unlike most other languages, in Rust these properties are exposed to and
170enforced by the type system. `flatbuffers::Table` and the generated table types
171are `Send + Sync`, indicating they may be freely shared across threads and data
172may be accessed from any thread which receives a const (aka shared) reference.
173There are no functions which require a mutable (aka exclusive) reference, which
174means all the available functions may be called like this.
175`flatbuffers::FlatBufferBuilder` is also `Send + Sync`, but all of the mutating
176functions require a mutable (aka exclusive) reference which can only be created
177when no other references to the `FlatBufferBuilder` exist, and may not be copied
178within the same thread, let alone to a second thread.
179
180## Useful tools created by others
181
182* [flatc-rust](https://github.com/frol/flatc-rust) - FlatBuffers compiler
183(flatc) as API for transparent `.fbs` to `.rs` code-generation via Cargo
184build scripts integration.
185
186<br>
187