Rust vs Go – A comparison

redox

Both Rust and Go are fairly recent languages. Go, aka golang was created in 2007 at Google primarily for systems programming, to help solve Google’s problems.  Rust was a personal project of Mozilla employee Graydon Hoare, then sponsored by Mozilla in 2009 and announced a year later. It’s more of a general purpose language than Go. In the rest of this article, I’ll pick out certain features that are common to both and then see how they differ. 

Simplicity or Complexity?

There’s a focus on simplicity in Go that’s absent from Rust. Compared to C++, a language that it’s designed to replace, Go has far fewer keywords, the language is small and simple.  Rust is more complex than Go while aiming to be a better C.  The focus is on safety, speed, and concurrency.

One of the problems Go aims to solve is compiler inefficiency in C and C++. It was noted that compiling a program made up of 2,000 files totalling 4.2 MB if concatenated in size resulted in the compiler reading in 8 GB, i.e. it was effectively reading each byte in the source files 2,000 times due to the way the compiler worked.  Go avoids this by importing packages, but it takes the compiled object, not the source. The result is a very fast compilation, probably faster than any other programming language. It’s worth noting that Borland used a similar system with their Borland Pascal compiler twenty-five years ago.

Concurrency

The move to multi-core to avoid the inevitable failure of Moore’s law has brought a new problem for programmers. A program that only uses one core on a CPU that has 8 or 12 cores (with hyper threading) is going to be seriously under powered. Both Rust and Go have concurrency built in, though in different ways.

Go has the philosophy: <i>Do not communicate by sharing memory; instead, share memory by communicating</i>. It supports go-routines as part of its CSP (Communicating Sequential Processes). Each go-routine is a process that runs independently of the main process and continues until it’s terminated, which is usually by hitting a return statement. You start a go-routine by running a function with the word go. Communication channels which are first class citizens (you can declare them, pass them as variables) are set up to move typed data between go-routines. They are a bit like a UNIX pipe.

There are no restrictions on how go-routines access shared data, and that makes race conditions possible. Unless a program synchronises via channels or other means, writes from one go-routine might be partly, entirely, or not at all visible to another, often with no guarantees about the ordering of writes. Furthermore, Go’s internal data structures like interface values, slice headers, hash tables, and string headers are not immune to race conditions, so type and memory safety can be violated in multi threaded programs that modify shared instances of those types without synchronization.

Rust has a library for threads (std::thread) and has been designed to prevent race conditions. This came about because Rust manages and enforces ownership of data. For a much detailed explanation of how it does this (too long for this article) read <a href=”https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.html” target=”_blank” title=”Link to Fearless Concurrency with Rust”>Fearless Concurrency with Rust</a>, a blog entry from the Rust team.

Types

Go has a rich set of types and can declare types based on others. For example.

<pre>

type T1 string

type T2 T1

var fred T1

</pre>

However Go is not object oriented. But you can add methods to types, for example onto a struct, and you can specify interface types which are implemented by any type that implements the methods in the interface. As almost any type can have methods attached, almost anything can satisfy an interface.

Rust is perhaps more traditional, in having a large list of types – for integers alone there are i8, i16, i32, i64, u8, u16, u32, u64, isize, usize, f32, and f64. No prizes for guessing what most of those mean. The two isize and usize depend upon the size of a pointer of the underlying machine. Then there are str (string), arrays, tuples, slices of other data and functions. (Yes functions have a type).

Variables are immutable unless defined with the word mut.

<pre>

let fred=5;

fred=7; // compiler says no

let mut dave = 4;

dave=8;

</pre>

Mutable references!

<pre>

let mut x = 10;  // Can change x later

let y = &mut x;

</pre>

Here y points to x and you can change the value of x with *y but can’t change y to be a reference to anything else.

<pre>

*y= 8;  // dereferencing a reference.

</pre>

Rust has three types of smart pointers. It can access raw pointers but that is unsafe.  Rust also has generics in the form of parametric polymorphism. Think of writing functions like Sort that work on ints, floats etc.

Garbage Collection

Go has garbage collection. It helps concurrency by reducing the need to track memory management of objects passed between threads. Also, the programmer has control over memory layout and allocation and this can be used to keep the overhead down.

There’s no garbage collector in Rust but it tracks ownership of objects by variables and calls the destructor when the variable goes out of scope. Rust achieves safety by having control over ownership and enforcing it at compile time. Consider this:

<pre>

let v = vec![1, 2, 3];

let v2 = v;

</pre>

After the second line, you can’t use v again to access the vector holding 1,2 and 3. Ownership has gone to v2. Some types like i32 include a Copy trait so the example below is ok. Both examples are from the <a href=”https://doc.rust-lang.org/book/” target=”_blank” title=”Link to Rust Book”>Rust book</a>.

<pre>

let v = 1;

let v2 = v;

</pre>

Conclusions

Rust reached version 1.0 in May 2015 and version 1.7 was released in March 2016. A Unix like micro-kernal operating system Redox OS has been written in Rust by developer Jeremy Soller and runs on real hardware. It’s under development and is on Github.

Go version 1.6 was released in November 2015. It has had quite some takeup outside Google as well as within, where the intention seems to be to replace many older technology projects. It’s used in Docker for deploying Linux containers, in Dropbox replacing Python in some cases. For a longer list see Notable users on the Wikipedia page for Go . To get a feel for Go uses which are very wide, the Go WIki on Github has a very long list of projects including 14 games!

My impression is that Go with the earlier lead has built a substantial fan base but Rust is coming quickly from behind. On Reddit programming sub-groups both have very large communities with Go has 19.2K users and Rust has 14.7K.

 

Comments

One Response to “Rust vs Go – A comparison”

September 14, 2017 at 12:32 am, siva said:

Nice article ! Thank you.

I might consider Rust in the future (around 2019). As of now, I use Python for almost every task. (Algorithm development, Data Science, Image analysis etc..) It is fast enough and further we could use pypy compiler for speed (albeit with limitations). Golang is my other choice for some data science and server based applications.

Based on my experience, I think that speed of development, code readability across the team and maintenance are far more important than the speed of execution. A complex programming language could make us generate less maintainable code. History proves this. Perl is an amazing language with vast libraries. Using special symbols and weird syntax, perl users wrote very concise code. However, the main reasons for perl’s downfall were adding of new Object Oriented, functional and other confusing features with every perl 5 version (>= 5.8). There seemed no end to new things. This unnecessary complexity caused the programming community hate perl which already had a bad reputation of being called write-only language (not easily readable). Scala is great but due to its complexity, no one loves to use it. Rust might face a similar future.

Complex features help us to write highly expressive, efficient and concise code. However, the code written employing these advanced features will not be easily understood by all the team members. The creators of Golang clearly knew this and struck a right balance between simplicity and complex features.

My 2 Cents..

Reply

Post a Comment

Your email address will not be published.