1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
//! I’m Out to C
//!
//! - **Date:** April 3, 2019
//! - **Subject:** Using Rust’s Foreign Function Interface (FFI) with C!
//! - [**download mp3**][mp3]
//! - [**script**][script]
//!
//! [mp3]: https://www.podtrac.com/pts/redirect.mp3/cdn.newrustacean.com/file/newrustacean/e029.mp3
//! [script]: https://newrustacean.com/show_notes/e029/struct.script
//!
//! <audio style="width: 100%" title="e029: I’m Out to C" controls preload=metadata src="https://www.podtrac.com/pts/redirect.mp3/cdn.newrustacean.com/file/newrustacean/e029.mp3">
//!
//! Show Notes
//! ----------
//!
//! The code samples here directly match the things I described in the show, so
//! you will likely want to look at [`add`] and [`ffi::add`], then [`Point`],
//! [`translate`], and [`ffi::translate`] in that order.
//!
//! [`add`]: ./fn.add.html
//! [`ffi::add`]: ./ffi/fn.add.html
//! [`Point`]: ./struct.Point.html
//! [`translate`]: ./fn.translate.html
//! [`ffi::translate`]: ./ffi/fn.translate.html
//!
//! ### Links
//!
//! Other helpful Rust FFI discussions:
//!
//! - [<cite>The Rust Programming Language</cite>’s materials]
//! - the [nomicon]
//! - [Rust FFI Omnibus]
//! - the [Reference’s] discussion
//! - the associated Rust [API] docs
//!
//! [nomicon]: https://doc.rust-lang.org/beta/nomicon/ffi.html
//! [Rust FFI Omnibus]: http://jakegoulding.com/rust-ffi-omnibus/
//! [deals.manning.com/new-rustacean]: http://bit.ly/2OXnlEb
//! [<cite>The Rust Programming Language</cite>’s materials]: https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html#using-extern-functions-to-call-external-code
//! [Reference’s]: https://doc.rust-lang.org/1.33.0/reference/items/external-blocks.html
//! [API]: https://doc.rust-lang.org/1.33.0/std/keyword.extern.html
//!
//! Sponsors
//! --------
//!
//! Thanks to Manning for sponsoring the show *and* giving all of you a 40%-off
//! discount on their whole store (but especially Carol Nichols' and Jake
//! Goulding's _Rust in Motion_ video content and the _Rust in Action_ MEAP!) at
//! [deals.manning.com/new-rustacean][manning]
//!
//! [manning]: http://bit.ly/2OXnlEb
//!
//! ### Patreon Sponsors
//!
//! - Adam Green
//! - Aleksey Pirogov
//! - Alexander Kryvomaz
//! - Alexander Lozada
//! - Alexander Payne
//! - [Andrew Dirksen]
//! - Andrew Thompson
//! - [Anthony Deschamps]
//! - Anthony Scotti
//! - Arlen Haftevani
//! - [Arlo (Hyena)]
//! - Arun Kulshreshtha
//! - [Behnam Esfahbod]
//! - [Benjamin Manns]
//! - Benjamin Wasty
//! - Brandon 'Spanky' Mills
//! - Brian Casiello
//! - Brian Manning
//! - [Brian McCallister]
//! - [Bryan Stitt]
//! - Caryn Finkelman
//! - Cass Costello
//! - Cat Dad
//! - Chap Lovejoy
//! - [Charlie Egan]
//! - Chip
//! - [Chris Palmer]
//! - Christoffer Ceutz
//! - Dan Abrams
//! - Daniel
//! - Daniel Bross
//! - [Daniel Collin]
//! - [Daniel Mason]
//! - David Carroll
//! - David Hewson
//! - [Derek Morr]
//! - Doug Reeves
//! - [Douglas Correa]
//! - Edmund Kump
//! - [Eduard Knyshov]
//! - [Embark Studios]
//! - Eugene Bulkin
//! - [Evan Stoll]
//! - [Fabio (decathorpe)]
//! - [Fabio Correa]
//! - [Gaveen Prabhasara]
//! - [Graham Wihlidal]
//! - [Henri Sivonen]
//! - [Ian Jones]
//! - Hoàng Đức Hiếu
//! - Hugo Josefson
//! - "Jake ""ferris"" Taylor"
//! - Jako Danar
//! - James Cooper
//! - James Hagans II
//! - [Jason Bowen]
//! - [Jendrik Illner]
//! - Jerome Froelich
//! - [Joar Wandborg]
//! - [Johan Andersson]
//! - [John Rudnick]
//! - Jon
//! - Jonah
//! - [Jonathan Knapp]
//! - Jonathan Turner
//! - Joseph Hain
//! - Joseph Mou
//! - Joseph Schrag
//! - [Joe Percy]
//! - Justin Ossevoort
//! - Kai Yao
//! - Kazutaka Mise
//! - Keith Gray
//! - Kilian Rault
//! - Lee Jenkins
//! - Luca Schmid
//! - [Luiz Irber]
//! - Lukas Eller
//! - [Malnormalulo]
//! - [Martin Heuschober]
//! - Masashi Fujita
//! - Matt Rudder
//! - Matthew Brenner
//! - Matthias Ruszala
//! - [Max Jacobson]
//! - Max R.R. Collada
//! - [Messense Lv]
//! - Micael Bergeron
//! - [Michael Mc Donnell]
//! - [Michael Melanson]
//! - Michael Sanders
//! - [Nathan Sculli]
//! - [Nick Coish]
//! - Nick Gideo
//! - [Nick Stevens]
//! - [Nicolas Pochet]
//! - Oladapo Fadeyi
//! - Olaf Leidinger
//! - Oliver Uvman
//! - [Oluseyi Sonaiya]
//! - Ovidiu Curcan
//! - [Pascal]
//! - [Patrick O'Doherty]
//! - Paul Naranja
//! - Paul Osborne
//! - Peter Scholtens
//! - Peter Tillemans
//! - Pierre-Antoine Champin
//! - Ralph Giles
//! - [Ramon Buckland]
//! - Randy MacLeod
//! - Raph Levien
//! - Richard Dallaway
//! - Rob Tsuk
//! - [Robbie Clarken]
//! - Robert Chrzanowski
//! - [Ryan Blecher]
//! - [Ryan Osial]
//! - Scott Moeller
//! - [Sebastián Ramírez Magrí]
//! - [Simon Dickson]
//! - Simon G
//! - [Soren Bramer Schmidt]
//! - Steve Jenson
//! - Steven Knight
//! - Steven Murawski
//! - [Stuart Hinson]
//! - Tim Brooks
//! - Tim Süberkrüb
//! - Tom Prince
//! - Toolmaker's Guild
//! - Ty Overby
//! - Tyler Harper
//! - Victor Kruger
//! - Will Greenberg
//! - [William Roe]
//! - Zak van der Merwe
//! - Zachary Snyder
//! - [Zach Peters]
//! - Zaki
//!
//! [Andrew Dirksen]: https://github.com/bddap
//! [Anthony Deschamps]: https://github.com/adeschamps
//! [Arlo (Hyena)]: https://asonix.dog/@asonix
//! [Behnam Esfahbod]: https://github.com/behnam
//! [Benjamin Manns]: https://www.benmanns.com/
//! [Brian McCallister]: https://skife.org/
//! [Bryan Stitt]: http://www.stitthappens.com/
//! [Charlie Egan]: https://charlieegan3.com
//! [Chris Palmer]: http://red-oxide.org/
//! [Damien Stanton]: https://github.com/damienstanton
//! [Daniel Collin]: https://twitter.com/daniel_collin
//! [Daniel Mason]: https://github.com/gisleburt
//! [Daniel P. Clark]: https://6ftdan.com/
//! [David W. Allen]: http://GitHub.com/DataRiot
//! [Derek Morr]: https://twitter.com/derekmorr
//! [Douglas Correa]: http://learnrust.io/
//! [Eduard Knyshov]: https://github.com/edvorg
//! [Embark Studios]: https://www.embark-studios.com
//! [Evan Stoll]: https://github.com/evanjs
//! [Gaveen Prabhasara]: https://twitter.com/gaveen
//! [Fabio (decathorpe)]: https://decathorpe.com/
//! [Fabio Correa]: https://linkedin.com/in/feamcor
//! [Graham Wihlidal]: https://wihlidal.com/
//! [Henri Sivonen]: https://hsivonen.fi/
//! [Ian Jones]: https://www.ianmjones.com/
//! [Jason Bowen]: https://twitter.com/jwbowen
//! [Jendrik Illner]: https://www.jendrikillner.com/
//! [Joar Wandborg]: http://github.com/joar
//! [Johan Andersson]: https://www.embark-studios.com
//! [Jonathan Knapp]: https://www.coffeeandcode.com/
//! [Joe Percy]: http://joetdc.com/
//! [John Rudnick]: http://www.cindur.com/
//! [Luiz Irber]: http://luizirber.org/
//! [Malnormalulo]: https://twitter.com/Malnormalulo
//! [Martin Heuschober]: https://github.com/epsilonhalbe
//! [Max Jacobson]: https://twitter.com/maxjacobson
//! [Messense Lv]: https://github.com/messense
//! [Michael Mc Donnell]: https://www.linkedin.com/in/michaelmcdonnell/
//! [Michael Melanson]: https://www.michaelmelanson.net
//! [Nathan Sculli]: http://influential.co/
//! [Nick Coish]: http://github.com/ncoish
//! [Nick Stevens]: https://github.com/nastevens
//! [Nicolas Pochet]: https://github.com/n-pochet
//! [Oluseyi Sonaiya]: http://oluseyi.info/
//! [Pascal]: https://pascalhertleif.de/
//! [Patrick O'Doherty]: https://twitter.com/patrickod
//! [Philipp Keller]: https://twitter.com/hansapla
//! [Ramon Buckland]: http://www.inosion.com
//! [Robbie Clarken]: https://github.com/RobbieClarken/
//! [Ryan Blecher]: http://notryanb.github.io/
//! [Ryan Osial]: https://github.com/osialr
//! [Sebastián Ramírez Magrí]: https://www.twitter.com/sebasmagri
//! [Simon Dickson]: https://www.simonhdickson.com/
//! [Soren Bramer Schmidt]: http://prisma.io/
//! [Stuart Hinson]: http://stuarth.github.io/
//! [William Roe]: http://willroe.me
//! [Zach Peters]: https://github.com/zpeters
//!
//! (Thanks to the couple people donating who opted out of the reward tier, as
//! well. You know who you are!)
//!
//! ### Become a sponsor
//!
//! - <a href="https://www.patreon.com/newrustacean" rel="payment">Patreon</a>
//! - [Venmo](https://venmo.com/chriskrycho)
//! - [Dwolla](https://www.dwolla.com/hub/chriskrycho)
//! - [Cash.me](https://cash.me/$chriskrycho)
//! - [Flattr](https://flattr.com/profile/chriskrycho)
//! - [PayPal.me](https://paypal.me/chriskrycho)
//!
//!
//! Contact
//! -------
//!
//! - New Rustacean:
//!     + Twitter: [@newrustacean](https://www.twitter.com/newrustacean)
//!     + Email: [[email protected]](mailto:[email protected])
//! - Chris Krycho
//!     + GitHub: [chriskrycho](https://github.com/chriskrycho)
//!     + Twitter: [@chriskrycho](https://www.twitter.com/chriskrycho)

use std::os::raw::c_float;

#[doc(include = "../docs/e029-script.md")]
pub struct Script;

/// An example of a slightly more complex data structure we can use with FFI.
///
/// Note the `#[repr(C)]`, which tells Rust to make sure this struct is laid out
/// the way that the C ABI expects. That's *not* the way that Rust's own ABI
/// (which is unstable and can change at any time) might lay it out.
///
/// Note also that `x` and `y` are `c_float`, which is [a type alias][c_float]
/// for `f32`. We use it here to make explicit the interop, and also because it
/// is *possible* that it might change on some specific operating system. If we
/// went to compile for an operating system where C's `float` type were *not* a
/// 32-point floating bit number (and the C standard does not require it to be
/// anything but "a floating point number"), the compiler would let us know.
///
/// [c_float]: https://doc.rust-lang.org/1.33.0/std/os/raw/type.c_float.html
#[repr(C)]
#[derive(Debug, PartialEq)]
pub struct Point {
    pub x: c_float,
    pub y: c_float,
}

/// The module with all the unsafe code in it! You'll want to poke at this!
///
/// (Note that this is private, and we wouldn't normally make this kind of code
/// visible in documentation at all. Instead, we would *only* make public the
/// safe abstraction around it which we're providing at the root of this module
/// (see [e027]!). I've set a `RUSTDOCFLAGS` value in the `Makefile` at the root
/// of the repo so you get these docstring notes for it!)
///
/// [e027]: https://newrustacean.com/show_notes/e027/
mod ffi {
    use super::Point;
    use std::os::raw::{c_float, c_int};

    extern "C" {
        /// A *mostly*-trivial example: addition in C instead of in Rust. (See
        /// the docs for `e029::add` for why it *isn't* totally trivial.)
        ///
        /// You can use it in an `unsafe` block like so:
        ///
        /// ```rust,ignore
        /// unsafe {
        ///     let result = add(1, 2); // 3, of course!
        /// }
        /// ```
        pub(super) fn add(a: c_int, b: c_int) -> c_int;

        /// An example of a C function we can call with an object.
        ///
        /// You can use it in an `unsafe` block like so:
        ///
        /// ```rust,ignore
        /// let mut point = Point { x: 0.0, y: 0.0 };
        /// unsafe {
        ///     translate(&mut point, 12.3, 14.4);
        /// }
        /// assert_eq!(point, Point { x: 12.3, y: 14.4 });
        /// ```
        ///
        /// [e027]: https://newrustacean.com/show_notes/e027/
        pub(super) fn translate(point: *mut Point, by_x: c_float, by_y: c_float);
    }
}

/// A safe interface for the unsafe `ffi::add`.
///
/// Note that this particular check is as silly as calling out to C for addition
/// is, but it shows how you can provide a safe wrapper for a case where C's
/// implementation differences *might* actually matter to you.
///
/// While it might seem that something like addition is trivially safe, it turns
/// out to be *mostly* safe. The behavior of overflow for signed integers is
/// *not defined* for C. In Rust, it *is* defined, by [RFC #0560]: in modes
/// where `debug_assertions` are enabled, an overflow will cause a panic; in
/// modes where those assertions are not enabled (i.e. release mode), Rust wraps
/// them by [two's complement]. The net of that is that even something this
/// simple can have unexpected results when calling across the FFI boundary.
///
/// [RFC #0560]: https://github.com/rust-lang/rfcs/blob/master/text/0560-integer-overflow.md
/// [two's complement]: https://en.wikipedia.org/wiki/Two's_complement
///
/// ```rust
/// # use show_notes::e029::add;
/// assert_eq!(add(1, 2), Some(3));
/// ```
pub fn add(a: i32, b: i32) -> Option<i32> {
    if i32::max_value() - a >= b {
        unsafe { Some(ffi::add(a, b)) }
    } else {
        None
    }
}

/// A safe interface for the unsafe `ffi::translate` function.
///
/// In this case, there are no invariants we need to maintain other than those
/// which Rust *always* maintains, i.e. that the reference we have with
/// `&mut Point` is a valid reference (not `null`, actually points to a `Point`,
/// and so on). Since Rust guarantees this, we can simply *directly* call the
/// unsafe extern function here.
///
/// I explicitly included the cast `as *mut Point`, but an `&mut Point` will be
/// automatically converted to a `*mut Point` when needed, so it is unnecessary
/// from a compiler perspective. It may, however, be helpful for making your
/// intent clear to other users!
///
/// ```rust
/// # use show_notes::e029::{translate, Point};
/// let mut point = Point { x: 0.0, y: 0.0 };
/// translate(&mut point, 2.4, 4.8);
/// assert_eq!(point, Point { x: 2.4, y: 4.8 });
/// ```
pub fn translate(point: &mut Point, by_x: f32, by_y: f32) {
    unsafe {
        ffi::translate(point as *mut Point, by_x as c_float, by_y as c_float);
    }
}