@@ -6,24 +6,24 @@ Rust’s greatest strengths: a lack of a substantial runtime.
6
6
As organizations grow, they increasingly rely on a multitude of programming
7
7
languages. Different programming languages have different strengths and
8
8
weaknesses, and a polyglot stack lets you use a particular language where
9
- its strengths make sense, and use a different language where it’s weak.
9
+ its strengths make sense and a different one where it’s weak.
10
10
11
11
A very common area where many programming languages are weak is in runtime
12
12
performance of programs. Often, using a language that is slower, but offers
13
- greater programmer productivity is a worthwhile trade-off. To help mitigate
14
- this, they provide a way to write some of your system in C, and then call
15
- the C code as though it were written in the higher-level language. This is
13
+ greater programmer productivity, is a worthwhile trade-off. To help mitigate
14
+ this, they provide a way to write some of your system in C and then call
15
+ that C code as though it were written in the higher-level language. This is
16
16
called a ‘foreign function interface’, often shortened to ‘FFI’.
17
17
18
18
Rust has support for FFI in both directions: it can call into C code easily,
19
19
but crucially, it can also be called _ into_ as easily as C. Combined with
20
20
Rust’s lack of a garbage collector and low runtime requirements, this makes
21
21
Rust a great candidate to embed inside of other languages when you need
22
- some extra oomph.
22
+ that extra oomph.
23
23
24
24
There is a whole [ chapter devoted to FFI] [ ffi ] and its specifics elsewhere in
25
- the book, but in this chapter, we’ll examine this particular use- case of FFI,
26
- with three examples, in Ruby, Python, and JavaScript.
25
+ the book, but in this chapter, we’ll examine this particular use case of FFI,
26
+ with examples in Ruby, Python, and JavaScript.
27
27
28
28
[ ffi ] : ffi.html
29
29
@@ -40,18 +40,18 @@ optimizations can stack allocate particular numbers, but rather than relying
40
40
on an optimizer to do its job, we may want to ensure that we’re always using
41
41
primitive number types rather than some sort of object type.
42
42
43
- Second, many languages have a ‘global interpreter lock’, which limits
43
+ Second, many languages have a ‘global interpreter lock’ (GIL) , which limits
44
44
concurrency in many situations. This is done in the name of safety, which is
45
45
a positive effect, but it limits the amount of work that can be done at the
46
46
same time, which is a big negative.
47
47
48
48
To emphasize these two aspects, we’re going to create a little project that
49
- uses these two aspects heavily. Since the focus of the example is the embedding
50
- of Rust into the languages, rather than the problem itself, we’ll just use a
49
+ uses these two aspects heavily. Since the focus of the example is to embed
50
+ Rust into other languages, rather than the problem itself, we’ll just use a
51
51
toy example:
52
52
53
53
> Start ten threads. Inside each thread, count from one to five million. After
54
- > All ten threads are finished, print out ‘done!’.
54
+ > all ten threads are finished, print out ‘done!’.
55
55
56
56
I chose five million based on my particular computer. Here’s an example of this
57
57
code in Ruby:
@@ -69,7 +69,7 @@ threads = []
69
69
end
70
70
end
71
71
72
- threads.each {|t | t.join }
72
+ threads.each { |t | t.join }
73
73
puts " done!"
74
74
```
75
75
@@ -82,12 +82,12 @@ sort of process monitoring tool, like `top`, I can see that it only uses one
82
82
core on my machine. That’s the GIL kicking in.
83
83
84
84
While it’s true that this is a synthetic program, one can imagine many problems
85
- that are similar to this in the real world. For our purposes, spinning up some
85
+ that are similar to this in the real world. For our purposes, spinning up a few
86
86
busy threads represents some sort of parallel, expensive computation.
87
87
88
88
# A Rust library
89
89
90
- Let’s re-write this problem in Rust. First, let’s make a new project with
90
+ Let’s rewrite this problem in Rust. First, let’s make a new project with
91
91
Cargo:
92
92
93
93
``` bash
@@ -104,7 +104,7 @@ fn process() {
104
104
let handles : Vec <_ > = (0 .. 10 ). map (| _ | {
105
105
thread :: spawn (|| {
106
106
let mut _x = 0 ;
107
- for _ in (0 .. 5_000_001 ) {
107
+ for _ in (0 .. 5_000_000 ) {
108
108
_x += 1
109
109
}
110
110
})
@@ -129,7 +129,7 @@ src/lib.rs:3 fn process() {
129
129
src/lib.rs:4 let handles: Vec< _> = (0..10).map(| _| {
130
130
src/lib.rs:5 thread::spawn(|| {
131
131
src/lib.rs:6 let mut x = 0;
132
- src/lib.rs:7 for _ in (0..5_000_001 ) {
132
+ src/lib.rs:7 for _ in (0..5_000_000 ) {
133
133
src/lib.rs:8 x += 1
134
134
...
135
135
src/lib.rs:6:17: 6:22 warning: variable ` x` is assigned to, but never used, # [warn(unused_variables)] on by default
@@ -151,7 +151,7 @@ Finally, we join on each thread.
151
151
Right now, however, this is a Rust library, and it doesn’t expose anything
152
152
that’s callable from C. If we tried to hook this up to another language right
153
153
now, it wouldn’t work. We only need to make two small changes to fix this,
154
- though. The first is modify the beginning of our code:
154
+ though. The first is to modify the beginning of our code:
155
155
156
156
` ` ` rust,ignore
157
157
# [no_mangle]
@@ -161,7 +161,7 @@ pub extern fn process() {
161
161
We have to add a new attribute, ` no_mangle` . When you create a Rust library, it
162
162
changes the name of the function in the compiled output. The reasons for this
163
163
are outside the scope of this tutorial, but in order for other languages to
164
- know how to call the function, we need to not do that. This attribute turns
164
+ know how to call the function, we can’t do that. This attribute turns
165
165
that behavior off.
166
166
167
167
The other change is the ` pub extern` . The ` pub` means that this function should
@@ -178,7 +178,7 @@ crate-type = ["dylib"]
178
178
` ` `
179
179
180
180
This tells Rust that we want to compile our library into a standard dynamic
181
- library. By default, Rust compiles into an ‘rlib’, a Rust-specific format.
181
+ library. By default, Rust compiles an ‘rlib’, a Rust-specific format.
182
182
183
183
Let’s build the project now:
184
184
@@ -204,7 +204,7 @@ Now that we’ve got our Rust library built, let’s use it from our Ruby.
204
204
205
205
# Ruby
206
206
207
- Open up a ` embed.rb` file inside of our project, and do this:
207
+ Open up an ` embed.rb` file inside of our project, and do this:
208
208
209
209
` ` ` ruby
210
210
require ' ffi'
217
217
218
218
Hello.process
219
219
220
- puts " done!”
220
+ puts ' done!'
221
221
` ` `
222
222
223
223
Before we can run this, we need to install the ` ffi` gem:
@@ -241,7 +241,7 @@ done!
241
241
$
242
242
` ` `
243
243
244
- Whoah , that was fast! On my system, this took ` 0.086` seconds, rather than
244
+ Whoa , that was fast! On my system, this took ` 0.086` seconds, rather than
245
245
the two seconds the pure Ruby version took. Let’s break down this Ruby
246
246
code:
247
247
@@ -258,11 +258,11 @@ module Hello
258
258
ffi_lib ' target/release/libembed.so'
259
259
` ` `
260
260
261
- The ` ffi ` gem’s authors recommend using a module to scope the functions
262
- we’ll import from the shared library. Inside, we ` extend` the necessary
263
- ` FFI::Library ` module, and then call ` ffi_lib` to load up our shared
264
- object library. We just pass it the path that our library is stored,
265
- which as we saw before, is ` target/release/libembed.so` .
261
+ The ` Hello ` module is used to attach the native functions from the shared
262
+ library. Inside, we ` extend` the necessary ` FFI::Library ` module and then call
263
+ ` ffi_lib` to load up our shared object library. We just pass it the path that
264
+ our library is stored, which, as we saw before, is
265
+ ` target/release/libembed.so` .
266
266
267
267
` ` ` ruby
268
268
attach_function :process, [], :void
@@ -280,10 +280,10 @@ Hello.process
280
280
281
281
This is the actual call into Rust. The combination of our ` module`
282
282
and the call to ` attach_function` sets this all up. It looks like
283
- a Ruby function, but is actually Rust!
283
+ a Ruby function but is actually Rust!
284
284
285
285
` ` ` ruby
286
- puts " done! "
286
+ puts ' done!'
287
287
` ` `
288
288
289
289
Finally, as per our project’s requirements, we print out ` done! ` .
@@ -329,7 +329,7 @@ After that installs, we can use it:
329
329
var ffi = require(' ffi' );
330
330
331
331
var lib = ffi.Library(' target/release/libembed' , {
332
- 'process': [ 'void', [] ]
332
+ ' process' : [' void' , []]
333
333
});
334
334
335
335
lib.process ();
@@ -340,7 +340,7 @@ console.log("done!");
340
340
It looks more like the Ruby example than the Python example. We use
341
341
the ` ffi` module to get access to ` ffi.Library()` , which loads up
342
342
our shared object. We need to annotate the return type and argument
343
- types of the function, which are ' void' for return, and an empty
343
+ types of the function, which are ` void` for return and an empty
344
344
array to signify no arguments. From there, we just call it and
345
345
print the result.
346
346
0 commit comments