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