Skip to content

trpl: Proofread "Rust Inside Other Languages" #25464

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 24, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/doc/trpl/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
* [Learn Rust](learn-rust.md)
* [Guessing Game](guessing-game.md)
* [Dining Philosophers](dining-philosophers.md)
* [Rust inside other languages](rust-inside-other-languages.md)
* [Rust Inside Other Languages](rust-inside-other-languages.md)
* [Effective Rust](effective-rust.md)
* [The Stack and the Heap](the-stack-and-the-heap.md)
* [Testing](testing.md)
Expand Down
58 changes: 29 additions & 29 deletions src/doc/trpl/rust-inside-other-languages.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,24 @@ Rust’s greatest strengths: a lack of a substantial runtime.
As organizations grow, they increasingly rely on a multitude of programming
languages. Different programming languages have different strengths and
weaknesses, and a polyglot stack lets you use a particular language where
its strengths make sense, and use a different language where it’s weak.
its strengths make sense and a different one where it’s weak.

A very common area where many programming languages are weak is in runtime
performance of programs. Often, using a language that is slower, but offers
greater programmer productivity is a worthwhile trade-off. To help mitigate
this, they provide a way to write some of your system in C, and then call
the C code as though it were written in the higher-level language. This is
greater programmer productivity, is a worthwhile trade-off. To help mitigate
this, they provide a way to write some of your system in C and then call
that C code as though it were written in the higher-level language. This is
called a ‘foreign function interface’, often shortened to ‘FFI’.

Rust has support for FFI in both directions: it can call into C code easily,
but crucially, it can also be called _into_ as easily as C. Combined with
Rust’s lack of a garbage collector and low runtime requirements, this makes
Rust a great candidate to embed inside of other languages when you need
some extra oomph.
that extra oomph.

There is a whole [chapter devoted to FFI][ffi] and its specifics elsewhere in
the book, but in this chapter, we’ll examine this particular use-case of FFI,
with three examples, in Ruby, Python, and JavaScript.
with examples in Ruby, Python, and JavaScript.

[ffi]: ffi.html

Expand All @@ -40,18 +40,18 @@ optimizations can stack allocate particular numbers, but rather than relying
on an optimizer to do its job, we may want to ensure that we’re always using
primitive number types rather than some sort of object type.

Second, many languages have a ‘global interpreter lock’, which limits
Second, many languages have a ‘global interpreter lock’ (GIL), which limits
concurrency in many situations. This is done in the name of safety, which is
a positive effect, but it limits the amount of work that can be done at the
same time, which is a big negative.

To emphasize these two aspects, we’re going to create a little project that
uses these two aspects heavily. Since the focus of the example is the embedding
of Rust into the languages, rather than the problem itself, we’ll just use a
uses these two aspects heavily. Since the focus of the example is to embed
Rust into other languages, rather than the problem itself, we’ll just use a
toy example:

> Start ten threads. Inside each thread, count from one to five million. After
> All ten threads are finished, print out ‘done!’.
> all ten threads are finished, print out ‘done!’.

I chose five million based on my particular computer. Here’s an example of this
code in Ruby:
Expand All @@ -69,7 +69,7 @@ threads = []
end
end

threads.each {|t| t.join }
threads.each { |t| t.join }
puts "done!"
```

Expand All @@ -82,12 +82,12 @@ sort of process monitoring tool, like `top`, I can see that it only uses one
core on my machine. That’s the GIL kicking in.

While it’s true that this is a synthetic program, one can imagine many problems
that are similar to this in the real world. For our purposes, spinning up some
that are similar to this in the real world. For our purposes, spinning up a few
busy threads represents some sort of parallel, expensive computation.

# A Rust library

Let’s re-write this problem in Rust. First, let’s make a new project with
Let’s rewrite this problem in Rust. First, let’s make a new project with
Cargo:

```bash
Expand Down Expand Up @@ -129,7 +129,7 @@ src/lib.rs:3 fn process() {
src/lib.rs:4 let handles: Vec<_> = (0..10).map(|_| {
src/lib.rs:5 thread::spawn(|| {
src/lib.rs:6 let mut x = 0;
src/lib.rs:7 for _ in (0..5_000_001) {
src/lib.rs:7 for _ in (0..5_000_000) {
src/lib.rs:8 x += 1
...
src/lib.rs:6:17: 6:22 warning: variable `x` is assigned to, but never used, #[warn(unused_variables)] on by default
Expand All @@ -151,7 +151,7 @@ Finally, we join on each thread.
Right now, however, this is a Rust library, and it doesn’t expose anything
that’s callable from C. If we tried to hook this up to another language right
now, it wouldn’t work. We only need to make two small changes to fix this,
though. The first is modify the beginning of our code:
though. The first is to modify the beginning of our code:

```rust,ignore
#[no_mangle]
Expand All @@ -161,7 +161,7 @@ pub extern fn process() {
We have to add a new attribute, `no_mangle`. When you create a Rust library, it
changes the name of the function in the compiled output. The reasons for this
are outside the scope of this tutorial, but in order for other languages to
know how to call the function, we need to not do that. This attribute turns
know how to call the function, we can’t do that. This attribute turns
that behavior off.

The other change is the `pub extern`. The `pub` means that this function should
Expand All @@ -178,7 +178,7 @@ crate-type = ["dylib"]
```

This tells Rust that we want to compile our library into a standard dynamic
library. By default, Rust compiles into an ‘rlib’, a Rust-specific format.
library. By default, Rust compiles an ‘rlib’, a Rust-specific format.

Let’s build the project now:

Expand All @@ -204,7 +204,7 @@ Now that we’ve got our Rust library built, let’s use it from our Ruby.

# Ruby

Open up a `embed.rb` file inside of our project, and do this:
Open up an `embed.rb` file inside of our project, and do this:

```ruby
require 'ffi'
Expand All @@ -217,7 +217,7 @@ end

Hello.process

puts "done!"
puts 'done!'
```

Before we can run this, we need to install the `ffi` gem:
Expand All @@ -241,7 +241,7 @@ done!
$
```

Whoah, that was fast! On my system, this took `0.086` seconds, rather than
Whoa, that was fast! On my system, this took `0.086` seconds, rather than
the two seconds the pure Ruby version took. Let’s break down this Ruby
code:

Expand All @@ -258,11 +258,11 @@ module Hello
ffi_lib 'target/release/libembed.so'
```

The `ffi` gem’s authors recommend using a module to scope the functions
we’ll import from the shared library. Inside, we `extend` the necessary
`FFI::Library` module, and then call `ffi_lib` to load up our shared
object library. We just pass it the path that our library is stored,
which as we saw before, is `target/release/libembed.so`.
The `Hello` module is used to attach the native functions from the shared
library. Inside, we `extend` the necessary `FFI::Library` module and then call
`ffi_lib` to load up our shared object library. We just pass it the path that
our library is stored, which, as we saw before, is
`target/release/libembed.so`.

```ruby
attach_function :process, [], :void
Expand All @@ -280,10 +280,10 @@ Hello.process

This is the actual call into Rust. The combination of our `module`
and the call to `attach_function` sets this all up. It looks like
a Ruby function, but is actually Rust!
a Ruby function but is actually Rust!

```ruby
puts "done!"
puts 'done!'
```

Finally, as per our project’s requirements, we print out `done!`.
Expand Down Expand Up @@ -329,7 +329,7 @@ After that installs, we can use it:
var ffi = require('ffi');

var lib = ffi.Library('target/release/libembed', {
'process': [ 'void', [] ]
'process': ['void', []]
});

lib.process();
Expand All @@ -340,7 +340,7 @@ console.log("done!");
It looks more like the Ruby example than the Python example. We use
the `ffi` module to get access to `ffi.Library()`, which loads up
our shared object. We need to annotate the return type and argument
types of the function, which are 'void' for return, and an empty
types of the function, which are `void` for return and an empty
array to signify no arguments. From there, we just call it and
print the result.

Expand Down