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