1# Rust Language Bindings for Thrift
2
3## Getting Started
4
51. Get the [Thrift compiler](https://thrift.apache.org).
6
72. Add the thrift crate to your `Cargo.toml`.
8
9```toml
10thrift = "x.y.z" # x.y.z is the version of the thrift compiler
11```
12
133. Generate Rust sources for your IDL (for example, `Tutorial.thrift`).
14
15```shell
16thrift -out my_rust_program/src --gen rs -r Tutorial.thrift
17```
18
194. Use the generated source in your code.
20
21```rust
22// generated Rust module from Thrift IDL
23mod tutorial;
24
25use thrift::protocol::{TCompactInputProtocol, TCompactOutputProtocol};
26use thrift::protocol::{TInputProtocol, TOutputProtocol};
27use thrift::transport::{TFramedReadTransport, TFramedWriteTransport};
28use thrift::transport::{TIoChannel, TTcpChannel};
29
30use tutorial::{CalculatorSyncClient, TCalculatorSyncClient};
31use tutorial::{Operation, Work};
32
33fn main() {
34    match run() {
35        Ok(()) => println!("client ran successfully"),
36        Err(e) => {
37            println!("client failed with {:?}", e);
38            std::process::exit(1);
39        }
40    }
41}
42
43fn run() -> thrift::Result<()> {
44    //
45    // build client
46    //
47
48    println!("connect to server on 127.0.0.1:9090");
49    let mut c = TTcpChannel::new();
50    c.open("127.0.0.1:9090")?;
51
52    let (i_chan, o_chan) = c.split()?;
53
54    let i_prot = TCompactInputProtocol::new(
55        TFramedReadTransport::new(i_chan)
56    );
57    let o_prot = TCompactOutputProtocol::new(
58        TFramedWriteTransport::new(o_chan)
59    );
60
61    let mut client = CalculatorSyncClient::new(i_prot, o_prot);
62
63    //
64    // alright! - let's make some calls
65    //
66
67    // two-way, void return
68    client.ping()?;
69
70    // two-way with some return
71    let res = client.calculate(
72        72,
73        Work::new(7, 8, Operation::Multiply, None)
74    )?;
75    println!("multiplied 7 and 8, got {}", res);
76
77    // two-way and returns a Thrift-defined exception
78    let res = client.calculate(
79        77,
80        Work::new(2, 0, Operation::Divide, None)
81    );
82    match res {
83        Ok(v) => panic!("shouldn't have succeeded with result {}", v),
84        Err(e) => println!("divide by zero failed with {:?}", e),
85    }
86
87    // one-way
88    client.zip()?;
89
90    // done!
91    Ok(())
92}
93```
94
95## Code Generation
96
97### Thrift Files and Generated Modules
98
99The Thrift code generator takes each Thrift file and generates a Rust module
100with the same name snake-cased. For example, running the compiler on
101`ThriftTest.thrift` creates `thrift_test.rs`. To use these generated files add
102`mod ...` and `use ...` declarations to your `lib.rs` or `main.rs` - one for
103each generated file.
104
105### Results and Errors
106
107The Thrift runtime library defines a `thrift::Result` and a `thrift::Error` type,
108both of which are used throughout the runtime library and in all generated code.
109Conversions are defined from `std::io::Error`, `str` and `String` into
110`thrift::Error`.
111
112### Thrift Type and their Rust Equivalents
113
114Thrift defines a number of types, each of which is translated into its Rust
115equivalent by the code generator.
116
117* Primitives (bool, i8, i16, i32, i64, double, string, binary)
118* Typedefs
119* Enums
120* Containers
121* Structs
122* Unions
123* Exceptions
124* Services
125* Constants (primitives, containers, structs)
126
127In addition, unless otherwise noted, thrift includes are translated into
128`use ...` statements in the generated code, and all declarations, parameters,
129traits and types in the generated code are namespaced appropriately.
130
131The following subsections cover each type and their generated Rust equivalent.
132
133### Primitives
134
135Thrift primitives have straightforward Rust equivalents.
136
137* bool: `bool`
138* i8: `i8`
139* i16: `i16`
140* i32: `i32`
141* i64: `i64`
142* double: `OrderedFloat<f64>`
143* string: `String`
144* binary: `Vec<u8>`
145
146### Typedefs
147
148A typedef is translated to a `pub type` declaration.
149
150```thrift
151typedef i64 UserId
152
153typedef map<string, UserId> MapType
154```
155```rust
156pub type UserId = i64;
157
158pub type MapType = BTreeMap<String, Bonk>;
159```
160
161### Enums
162
163A Thrift enum is represented as a Rust enum, and each variant is transcribed 1:1.
164
165```thrift
166enum Numberz
167{
168    ONE = 1,
169    TWO,
170    THREE,
171    FIVE = 5,
172    SIX,
173    EIGHT = 8
174}
175```
176
177```rust
178#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
179pub enum Numberz {
180    ONE = 1,
181    TWO = 2,
182    THREE = 3,
183    FIVE = 5,
184    SIX = 6,
185    EIGHT = 8,
186}
187
188impl TryFrom<i32> for Numberz {
189    // ...
190}
191
192```
193
194### Containers
195
196Thrift has three container types: list, set and map. They are translated into
197Rust `Vec`, `BTreeSet` and `BTreeMap` respectively. Any Thrift type (this
198includes structs, enums and typedefs) can be a list/set element or a map
199key/value.
200
201#### List
202
203```thrift
204list <i32> numbers
205```
206
207```rust
208numbers: Vec<i32>
209```
210
211#### Set
212
213```thrift
214set <i32> numbers
215```
216
217```rust
218numbers: BTreeSet<i32>
219```
220
221#### Map
222
223```thrift
224map <string, i32> numbers
225```
226
227```rust
228numbers: BTreeMap<String, i32>
229```
230
231### Structs
232
233A Thrift struct is represented as a Rust struct, and each field transcribed 1:1.
234
235```thrift
236struct CrazyNesting {
237    1: string string_field,
238    2: optional set<Insanity> set_field,
239    3: required list<
240         map<set<i32>, map<i32,set<list<map<Insanity,string>>>>>
241       >
242    4: binary binary_field
243}
244```
245```rust
246#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
247pub struct CrazyNesting {
248    pub string_field: Option<String>,
249    pub set_field: Option<BTreeSet<Insanity>>,
250    pub list_field: Vec<
251        BTreeMap<
252            BTreeSet<i32>,
253            BTreeMap<i32, BTreeSet<Vec<BTreeMap<Insanity, String>>>>
254        >
255    >,
256    pub binary_field: Option<Vec<u8>>,
257}
258
259impl CrazyNesting {
260    pub fn read_from_in_protocol(i_prot: &mut TInputProtocol)
261    ->
262    thrift::Result<CrazyNesting> {
263        // ...
264    }
265    pub fn write_to_out_protocol(&self, o_prot: &mut TOutputProtocol)
266    ->
267    thrift::Result<()> {
268        // ...
269    }
270}
271
272```
273##### Optionality
274
275Thrift has 3 "optionality" types:
276
2771. Required
2782. Optional
2793. Default
280
281The Rust code generator encodes *Required* fields as the bare type itself, while
282*Optional* and *Default* fields are encoded as `Option<TypeName>`.
283
284```thrift
285struct Foo {
286    1: required string bar  // 1. required
287    2: optional string baz  // 2. optional
288    3: string qux           // 3. default
289}
290```
291
292```rust
293pub struct Foo {
294    bar: String,            // 1. required
295    baz: Option<String>,    // 2. optional
296    qux: Option<String>,    // 3. default
297}
298```
299
300## Known Issues
301
302* Struct constants are not supported
303* Map, list and set constants require a const holder struct
304