Choosing Elixir over Rust and Golang

Surya Singh
6 min readMar 2, 2022

Choosing a language is always a compromise between benefits and shortcomings. I firmly believe in not picking a programming language for its popularity. We must select a language based on our requirements. Though all three are excellent language, we went with Elixir based on our needs. The most significant benefits for us to pick Elixir was its:

  • fault-tolerance capability
  • simple syntax and slight learning curve
  • highly scalable
  • support of Erlang/BEAM
  • distributed nodes and information sharing between them
Photo by Nathan Dumlao on Unsplash

Comparison

When, Why, and Who started?

José Valim started Elixir in 2012. He realized that at the current time, we are facing the same kind of problem while building software or web apps that the Erlang team was meeting with telephony. The intention was to create a highly efficient language by leveraging the Erlang VM (BEAM).

Erlang was designed & developed by Joe Armstrong, Robert Virding, and Mike Williams at Ericsson Computer Science Laboratory (CSLab). It got its name from one of the engineers and a syllabic abbreviation of “Ericsson Language.” It was started as an extension to Prolog in 1981 to build the next-generation telephonic switches, and by the second half of the 1980s, it was clear that it was suitable for telephonic programming. The requirement was to keep the telephonic switches soft in real-time and always be available. Work on Erlang VM (BEAM) started in later years. The Erlang BEAM compiler replaced the original JAM(Joe’s Abstract Machine) compiler in 1998. In 1993 the first commercial version of Erlang was released. Later it was made open source by Ericsson in 1998.

Rust was initially started by Mozilla employee Graydon Hoare as a personal project in 2006 and came under the Mozilla umbrella in 2009.

Go was initially started in late 2007 at Google and later made open source. It was designed and developed by Robert Griesemer, Rob Pike, and Ken Thompson to make development more productive.

What and How?

Elixir is a functional(only function language amongst the three, also not a purely functional language), general-purpose programming language. It is a strong, dynamic typed, and compiled language.

  1. It provides concurrency with the help of the BEAM, which specializes in concurrent systems in telephony. High scalability and concurrency come from BEAM(Bogdan Erlang Abstract Machine), the Erlang virtual machine. The BEAM cluster can initiate multiple BEAM for one CPU. It typically runs one thread per core, but it can configure it to use differently.
  2. It supports fault-tolerance and gives reliability as processes are executed separately, garbage is collected independently, and no resources are shared, providing true process isolation. They have a dynamic stack and can start small and grow huge as the requirement grows, and hence we can have millions of processes running without going out of memory.
  3. Supervisors can control the life cycle of any supervised process. The scheduler can interrupt tasks where a process is running and terminate the supervisor’s unresponsive task. It is cheap to switch context here because it is preemptive and not cooperative (detailed difference link.) So one single process will never take down the entire system.
  4. Open Telecom Platform, OTP, an integral part of the Erlang standard library, follows a process-oriented programming paradigm. It also provides a relational and automatically distributed database called Mnesia.
  5. Erlang is built on the idea to run forever.
  6. It has a slight and straightforward learning curve, and hence it is easy to get started with a new project or onboard a new member to the project.
  7. Popular framework: Phoenix

What is a process: Process is the basic unit of abstraction allowing writing large fault-tolerant software systems. It is not the same as the OS process. Instead, it’s an asynchronously ultra-lightweight, virtual machine-level process that is entirely independent, i.e., they do not share the memory. BEAM preemptively schedules it. The standard library function spawn invokes a process that is a sequential program. Processes have a mailbox to receive messages in a queue and process them accordingly. Start/stop the process is a cheap or lightweight operation. It gives process mailbox’s a synchronizing capability in an asynchronous system. Processes can send messages using send method.

Rust is a static, strong typed, and compiled language. After the release, developers constantly loved it and came at the top of the Stack Overflow Developer Survey in 2016, 2017, 2018, & 2019 resulting in a very fast-growing community.

  1. The structure is present over Object-oriented, not entirely Object-oriented. Traits are present, which is like Interface in Java. They do not have inheritance but support it using a/is a model.
  2. It provides memory safety because of garbage collection at compile time and better performance.
  3. Rust does not have garbage collection but has a borrow checker.
  4. If multi-threads are not thread-safe, you will get a type error at the compiled time.

Go is a procedural language and similar to C. It is a strong and static typed language. They originally started it with a dislike towards C++ and a desire to build a high-performance server-side language.

  1. Not quite Object-oriented. No inheritance. Provide object type with struct.
  2. Fast compiler
  3. Go has garbage collection.
  4. Go achieves concurrency with the help of goroutines and channels. It follows the Communicating Sequential Processes (CSP) paradigm.
  5. Possible to create multiple goroutines in one thread.

- Strong static typed language: there is a restriction between the conversion of types, and it will check at compile time (Java, Rust & Go).
- Strong dynamic typed language: there is a restriction between the conversion of types, and it will check at run time(Erlang & Elixir).

Elixir, Go, and Rust all 3 belong to the family of message passing languages.

Message passing: It’s a technique for passing or sending messages while running a program. There are no shared objects; hence, they send or receive messages between any object, process, function, or anything. Simple to complex data can be sent or received between two objects or processes. It can be both synchronous or asynchronous. It can use to build Event-Driven systems.

What’s good:

Elixir:

  1. Web APIs
  2. Strong and dynamically(checked at runtime) typed.
  3. Soft real-time services can be built with socket connections like a chatbot or google docs. It’s not for hard real-time services (the difference between them is here).
  4. It provides high scalability and fault tolerance support.
  5. Concurrency is based on the actor model. It’s broadly similar to Agha’s classic actor model. This type of actor model is called Process-based actors. It defines as a computation that runs from start to completion. Instead, Agha’s classic actor model defines an actor almost as a state machine of behaviors and the logic to transition between those. The process-based actor model was developed initially at Ericsson while developing Erlang.
  6. It also supports pattern matching, which can help destructure more complex data types.
  7. Stable library and supportive community but not like Nodejs or Python but decent enough (few popular ones are here).
  8. Developer experience is good.

Rust:

  1. Strongly typed language
  2. Build to make highly concurrent and highly safe systems
  3. Concurrency is available, and it follows the actor model.
  4. The compiler is excellent and catches things in advance.
  5. Produce static binary in output for deployment.
  6. System for systems that need efficiency and performance like CPU-intensive applications.
  7. Async io is getting stable with the Tokio module.
  8. The average minor release is every six weeks.

Go:

  1. Strong and statically(checked at compile time) typed.
  2. Concurrency is achieved with goroutines and channels
  3. Produce static binary in output for deployment.
  4. Suitable for web-based systems.
  5. Stable library and supportive community.
  6. Provide support for reliable WebSocket connections.
  7. The event emitter library is present, inspired by the NodeJS event emitter.

What’s an Actor model: Actors are pieces of application which are loosely coupled and isolated from one another. Communication is explicit and done by messages. Actors may use message queues for communication. Messaging between sender and receiver is entirely decoupled and isolated. Receiver results do not affect the sender here, i.e., message delivery is not guaranteed.

What’s Communicating Sequential Process (CSP): Units are very tightly coupled. Because of tight coupling, the receiver result affects the sender here. The concurrent communication between processes is done by passing messages through channels. The sender passes synchronous messaging to the receiver.

What’s a little bad:

Elixir:

  1. Number crunching.
  2. High CPU-based like image processing.

Rust:

  1. Writing code is complex.
  2. The learning curve is higher.
  3. Less mature library and community.
  4. Compiler is slow
  5. It’s not a 100% self-supporting compiler, and it is implemented on the LLVM compiler.

Go:

1. Not great in error handling.

Adopters

Elixir: Whatsapp uses Erlang, Discord, Pinterest, Ziwo
Rust: Atlassian, Mozilla, Brilliant
Go: Google, Uber, DropBox

--

--