If you were expecting a showdown in which I go on to proclaim Ruby (or Rust) to be the best language ever, you can stop reading now. I have been programming Ruby for a few years and have only recently picked up Rust. This article serves to show how much I (do not) know about Rust and how it compares to Ruby in my point of view.
If you’ve never heard of it, Rust is a language championed by Mozilla that aims at replacing C++ as the language in which Firefox in written.
While learning Rust I came across the Rust for Rubyists book by Steve Klabnik, which I recommend, and on that book there is small program that shows how to create different kinds of monsters with the same interface. I tweaked it a bit and implemented it in Ruby as well, so it can be compared.
The Ruby version
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 |
|
We have two monsters each with a set of characteristics and an attack
method,
an instance of each of them is created, put into an array and finally they are
given order to attack. The call_attack
method is there in order to correctly
replicate the Rust version, it is a bit redundant here.
The Rust version
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 |
|
The code is similar, but we have something called a trait
, another thing
called struct
and yes, a main
function. Let’s look a bit closer at each of
the differences.
Comparing the implementations
The first and probably more obvious difference is that Ruby does not need an
interface definition, called a trait
in Rust, it relies on duck typing. What
that means is that Ruby trusts you not to use a monsters that does not respond
to attack
. On the other hand, Rust’s compiler ensures that all the monsters
have that function, throwing an error otherwise. Nothing to fancy here, it’s the
dynamic versus strong typing duality you are probably already aware of.
Even though Rust relies strongly on types, it does have a very good ability to
infer them, as you can see when the monsters
variable is assigned on line 52.
Another thing you don’t have with Rust are classes, the way to get something
close to a class is to have a struct
, which defines a set of variables, a
trait
which defines a set of functions and mixing then with impl TraitX for
StructY
.
Still on the topic of traits, you might have noticed that one of the functions
receives &self
as a parameter and the other returns Self
. Those are the
equivalent to an instance method and initialize
in Ruby.
The last difference I find worthy of note is that in Ruby most variables are pointers to the actual object and you have no control over that. In Rust you have three different types of pointers, owned, managed and borrowed. Having programmed in C a few years ago, I came to fear and respect pointers as the bringers of pain. Pointers in Rust are much nicer, mostly because of the language philosophy to put safety first, making it impossible for memory leaks and overflows to pass through the compile phase. It is also great with concurrency through a couple of constructs called tasks and channels.
It obvious that the Rust implementation has more code, but it also adds an extra layer of safety, which you might or might not want.
Good defaults
To be fair, Rust adds two “hidden” features. First, all variables are immutable by default and you have to make them explicitly mutable if you want.
1
|
|
Secondly, all functions, structs and traits are private by default, so if I was to try and use them in another file:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
I would get a bunch of compile time errors such as:
1
|
|
Which just to prove the point can be fixed with:
1 2 3 4 5 6 |
|
Conclusion
I have been fiddling with Rust for a very small amount of time, but I’m really liking how conceptually different it is from Ruby. It is a great language to learn some new ideas, and also to go down a level to where you have to use pointers.