Rust — non-JS language from a developer’s perspective and how it differs from JavaScript

calendar icon
29 Mar
2024
21 Nov
2023
scroll

Rust is a language that promises both performance and safety. Its increasing adoption in various sectors of software development makes it a desirable choice for developers seeking new challenges. In this article, we will journey through the key aspects of Rust and its role in web development, comparing it with a familiar face in the world of programming — JavaScript.

(Non)-JS languages

First, let’s remind ourselves of some crucial info about the so-called JS and non-JS languages. As you may know, JavaScript is one of the most popular programming languages in the world. According to the TIOBE index for 2022, JavaScript takes 7th place in terms of popularity, seeping into a great range of industries. They use JavaScript for servers, IoT, for instance, the Johnny-Five project, mobile and naturally web applications, and even game dev (albeit browser games, but nonetheless, JS has gotten even there).

However, it’s worth noting that the popularity of JavaScript is also attributed to the 
so-called reverse flow, when non-JS-related languages penetrate the predominantly JavaScript-dominated environment. By these languages, I mean the ones that we can’t even compile to JS.

To reinforce your brand identity, you can put your logo and play around with your corporate colors in the footer.
Top languages according to the TIOBE index

Below, you can see some popular applications used by developers. And what’s special about them is that most of them partially use JavaScript or don’t use it at all.

Top languages according to the TIOBE index

As you can see, most of them are written using Rust. So why’s that? I’ll get to that in a minute.

The State of JS 2021 survey results

It may be surprising to some of you, but Rust is actually much more popular than you might imagine. See it for yourself — the table above demonstrates the propagation of non-JavaScript languages, and Rust ranks quite high there.

Rust is used by almost 10% of the developers who use both JS and another language.

Specifics of Rust in web development

So, Rust… what’s so special about it? Well, it is a systems programming language that emphasizes safety, speed, and concurrency. Its design aims to prevent memory-related errors, such as null pointer dereferencing and buffer overflows, without compromising performance.

There are four main reasons why Rust is gaining traction:

  • Speed. Rust is a compiled language that generates machine code. 
That’s why applications written with Rust run incredibly fast.
  • Safety. Its compiler prevents undefined behavior that can lead to unexpected results or crashes, thanks to the failsafe integrated into the mechanism.
  • Correctness. Rust is still in active development, and the language is constantly being improved. The core of the language has remained largely unchanged since the first stable version was released in 2015 — every new feature added to the language was designed not to disrupt anything introduced earlier. That means that every addition or fix is well thought out and this will likely remain true in the future.
  • Extensibility. One more benefit of this language is the extensibility achieved through the use of macros. In Rust, the code that will be run is written as usual, while macros are used to create a stream of standard syntax tokens that will then be converted into Rust code. It means that if you want to add more features or write code more efficiently, you can create your own syntax and utilize it through macros. This allows you to expand the capabilities of the language as macros transform unrecognizable syntax into the standard one.
Macros empower developers to create their own syntax

The language’s progress is slow because its developers thoroughly test new features before introducing them. This ensures that new features do not conflict with the planned ones, impact performance, or introduce other unwanted side effects. Such attention to detail is one of the reasons why Rust is a popular language — it’s not just easy to learn and use but also very reliable.

JavaScript vs Rust — what’s the difference?

Now, let’s look into some of Rust’s peculiarities. To make it easier for you, I’ll make a comparison of the same code in JavaScript. Indeed, in the realm of modern programming, comparing JavaScript and Rust might feel like juxtaposing an apple with an orange. The two have been conceived for different domains and purposes. Yet, as we dive deeper, the contrasts and complements become clearer.

Data types

Primarily, the difference between JavaScript and Rust is reflected in their data types. JavaScript is dynamically typed, while Rust is statically typed. This means that variables in JavaScript don’t need to be declared with a specific type, while variables in Rust may or may not be, but they will be bound to a specific data type, either inferred or declared one.

JavaScript has a relatively small number of data types, including numbers, strings, booleans, and objects. Rust has a much larger number of data types, including scalar types (such as i32, f64, and bool), compound types (such as tuples and arrays), and even user-defined types.

The different approaches to data types in JavaScript and Rust have different implications for safety and performance. Dynamic typing can make JavaScript code more concise and flexible, but it can also lead to errors at runtime. Static typing can make Rust code more safe and predictable, but it can also make it more verbose and difficult to write.

Rust has a much larger number of data types than JS

Rust’s data types are designed to be both safe and expressive. Its scalar types are divided into integers, floating-point numbers, and Booleans. The integers are further divided into signed and unsigned types, and they are also divided by the number of bits they take up. Rust doesn’t have a BigInt type, but it does have a 128-bit unsigned integer type that is more than sufficient for most applications.

Rust also has a number of compound types, such as tuples and arrays. A tuple is indeed a heterogeneous collection, meaning it can store elements of different data types within the same tuple. In contrast, an array is typically a homogeneous collection, where all elements must be of the same data type.

With Rust, users can define their own data types. This can be useful for creating more complex data structures or for encapsulating data.

Variables

When it comes to variables, there are 3 types in Rust: basic, constant, and static.

The first two lines of code in JS and Rust are the same

If you want to declare a mutable variable, you need to add “mut” to the line. Without it, you will declare a constant. But if you look at the third line in the example code, you’ll see that it also declares a constant. However, there’s a huge difference between the 2nd and the 3rd lines — any variable with a “let” keyword cannot be declared in the module scope.

Also, every variable declared with the “const” keyword should have a clearly declared type. And the value that you can write there has to be evaluated as the compile-time expression, so the compiler can execute it and assign the result to the variable. If this evaluation doesn’t take place, the variable becomes invalid, which imposes a significant limitation on the values that can be determined during compilation. Essentially, you can only work with constant expressions in this scenario.

In addition, it’s important to note that you cannot include a function call as you would in a runtime operation. Instead, only constant functions can be used to obtain a value for the const variable.

Last but not least, there are static variables. They can be mutable or constant, just like let and const variables. However, static variables can be declared in the global scope, which is not allowed for let variables.

Remember that mutating a static variable is an unsafe operation.

If you mutate a static variable, your code will be deemed unsafe, so it’s a really bad idea, except for some rare cases.
{{yevhen-k}}

Static variables in Rust are special because they have fixed addresses in memory. This makes them more efficient than other types of variables, but it also means that they cannot be moved.

Here, I want to mention such a concept as the ownership transfer, as it’s used to manage the lifetime of data. This idea implies that variables are moved instead of being copied because move semantics are more efficient than copy semantics. So, it’s important to be aware of the ownership transfer rules when using static variables.

If a variable is passed to a function, a compound type, or another variable, it transfers its ownership, so the previous variable is no longer valid.

The example below shows that passing a variable to a function makes it unusable. If you try to run this code, you will get an error.

The variable is moved, and its ownership is transferred to the new owner

Functions

JavaScript functions can be created using function declarations or function expressions, while Rust utilizes function declaration and lambda function (closure). They have slightly different syntaxes and unique distinctions.

The function declaration requires the fn keyword followed by the name of a function and a list of parameters followed by the return type. When a function is declared, it cannot capture the environment values in which it is defined.

The lambda function doesn’t require the fn keyword to be present and doesn’t have a name, and in contrast to the function declaration, it can capture values of their environment, so you can use it later.

Rust function parameters must have types

Even so, function declaration and lambda function have some similarities. 
For example, both of them are high-class objects. This means that the function and the closure can be used in an expression like any other type.

Pattern Matching

JavaScript doesn’t have native support for pattern matching in the same way Rust does. It often relies on a series of if or switch statements to achieve the same result.

Rust, on the other hand, offers a powerful match construct that allows you to describe the shape of data you expect declaratively. This is a great feature taken straight from functional programming. It can be used to check nested objects for compatibility with your pattern.

Rust’s pattern matching makes the code more concise
Pattern matching allows you to specify exactly what you want to be checked, which can make your code more concise and easier to read.

Objects

In Rust, there are no classes, object literals, or inheritance. Instead, Rust uses a concept called structs to define data structures. A struct can consist of fields, but it doesn’t include methods. In other words, a struct serves as a data description, not a blueprint for an object. To add functionality to a struct, Rust uses a concept called impl blocks, which can contain functions that are associated with that struct.

Associated functions can receive a struct object as the first parameter and are referred to as methods. If not, functions are just bound to the struct definition and should be reached via :: operator from it.

Despite not having objects in the traditional sense, Rust still has a lot of features that are similar to object-oriented programming languages.

If you look at the 6th line in the example below, you can see “fn new.” This is an associated function without referencing the struct itself. Being similar to the constructor function in JS, it simply creates a new instance of the struct.

You can name it however you like, but new is a relatively concise and appropriate name for this function.

Objects help organize, modularize, and reuse code

In addition, there is also fn say() function that has a &self first parameter, which always references the structure that is executing the function. This is a reserved keyword in Rust. You can see a similar concept in Python, where the first parameter of a method always contains a reference to its object.

Interfaces

In the dynamic world of JavaScript, there’s no built-in concept of interfaces as in some statically typed languages. TypeScript, a superset of JavaScript, however, introduces this feature.

Rust uses traits to achieve a similar purpose. Traits define shared behavior across types. They encapsulate methods and can be implemented by different structs or enums.

Traits can be used to add functionality to types and to make generic code

In Rust, there are three primary traits that relate to function-like behavior and the ability to call functions or closures:

  1. Fn is for functions or closures that can be called multiple times without mutating any captured variables.
  2. FnMut is for functions or closures that mutate the captured variables. The closure will need mutable access to its environment.
  3. FnOnce is for functions or closures that take ownership of captured variables and can be called exactly once.

The difference between Fn and FnMut is that FnMut allows the closure to mutate the captured variables, while Fn does not. The difference between FnMut and FnOnce is that FnOnce can only be called once, while FnMut can be called multiple times.

Modules

When it comes to organizing files, Rust has a few conventions that must be followed. All source code must be located in the src directory. This is because Rust uses a concept called crates, which are self-contained units of code that can be compiled and linked together. The src directory is the default crate root, which means that all source code for a crate must be located there.

Rust also has two reserved files, main.rs and lib.rs. The main.rs file is the entry point for the program, and it is where the main function is defined. The lib.rs file is the library file, and it contains functions and types that can be used by other crates.

As previously mentioned, both main.rs and lib.rs serve as entry points for each crate. When a crate contains multiple modules, we should establish a clear path from these entry points to reach any module within the crate. Think of it as navigating a tree with one or two primary roots — to reach a specific branch, you start from the root, follow the branches, and eventually arrive at the desired leaf node.

To attain such behavior, each module must be registered within another module, creating a hierarchy that makes them reachable. While you cannot directly register a deeply nested module (file) in a top-level module (file) using Unix-like relative paths, Rust provides an alternative approach. You can register each module in another module at the same level or within the parent module, ensuring that the module’s name corresponds to the folder where the imported module resides. This guarantees a logical and accessible module structure within your project.

After registering the module, it can be used anywhere in the modules tree.

Rust’s module system makes large codebases easier to navigate

Non-JS languages are worth exploring

JavaScript is a popular language, but there are other languages out there offering different strengths and weaknesses. Rust is a good example of this. It is a statically typed language that is designed to be safe and fast. This makes it a good choice for systems programming, where safety and performance are critical.

The exploration of non-JS languages, like Rust, offers developers a chance to expand their skills, discover new paradigms, and approach problems from diverse perspectives. If you’re eager to delve deeper into Rust or explore other programming languages, consider visiting our Development Section on the blog. Here, you’ll not only find valuable insights about Rust, JavaScript, Python, and more but also make the most of your time in an engaging and productive manner. Thanks for reading and take care!

Writing team:
Dmуtro
Editor
Viktoriia
Technical writer
Have a project
in your mind?
Let’s communicate.
Get expert estimation
Get expert estimation
expert postexpert photo

Frequently Asked Questions

No items found.
copy iconcopy icon

Ready to discuss
your project with us?

Please, enter a valid email
By sending this form I confirm that I have read and accept the Privacy Policy

Thank you!
We will contact you ASAP!

error imageclose icon
Hmm...something went wrong. Please try again 🙏
SEND AGAIN
SEND AGAIN
error icon
clutch icon

Our clients say

The site developed by Halo Lab projected a very premium experience, successfully delivering the client’s messaging to customers. Despite external challenges, the team’s performance was exceptional.
Aaron Nwabuoku avatar
Aaron Nwabuoku
Founder, ChatKitty
Thanks to Halo Lab's work, the client scored 95 points on the PageSpeed insights test and increased their CR by 7.5%. They frequently communicated via Slack and Google Meet, ensuring an effective workflow.
Viktor Rovkach avatar
Viktor Rovkach
Brand Manager at felyx
The client is thrilled with the new site and excited to deploy it soon. Halo Lab manages tasks well and communicates regularly to ensure both sides are always on the same page and all of the client’s needs are addressed promptly.
Rahil Sachak Patwa avatar
Rahil Sachak Patwa
Founder, TutorChase

Join Halo Lab’s newsletter!

Get weekly updates on the newest stories, posts and case studies right in your mailbox.

Thank you for subscribing!
Please, enter a valid email
Thank you for subscribing!
Hmm...something went wrong.