Getting started with Rust as a web developer
Tags: rust, javascript, todayilearned, programming
Published: 2022-06-21 17:13:46

> Please note that I'm still actively learning the language. I'll do my best to describe my current knowledge, but if you find anything to be incorrect, please contact me and I'll see to put it right.

Why Rust?
---------

There were a few other candidates, such as Go and C. I settled with Rust because it's

*   a relatively young system language with many 'future-proof' use cases (such as [WASM](https://www.rust-lang.org/what/wasm) and [Cryptocurrencies](https://github.com/solana-labs/solana))
*   fundamentally different from Javascript (but perfectly compatible if necessary).

To name a few key distinctions:

| Characteristic | Rust | Javascript |
| --- | --- | --- |
| Implementation | Compiled language | Interpreted language |
| Typing|Strictly static|Dynamic|
| Memory |Borrow checker |Garbage Collector |

[Many](https://www.trio.dev/blog/golang-vs-rust) [sources](https://blog.logrocket.com/when-to-use-rust-when-to-use-golang/) [on](https://www.reddit.com/r/golang/comments/jp4kvp/rust_vs_go/) [the](https://github.com/hiroaki-yamamoto/go-vs-rust) [internet](https://news.ycombinator.com/item?id=15266066) assign Rust a steep learning curve. It'd be a great challenge to learn a [system programming language](https://en.wikipedia.org/wiki/System_programming_language).

The following article focuses on the four above characteristics. There will be descriptions and some code examples for you to follow.

> For your best reading experience, you should be proficient with Javascript and the Linux / Windows terminal.

Install Rust
------------

Before starting, you'll need `rustup` to install the Rust compiler and Cargo, Rust's package manager. For Linux and macOS, install it with the following script:

```bash
curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh
```

For Windows, download and run the `rustup-init.exe`. You can find it here:  
[https://forge.rust-lang.org/infra/other-installation-methods.html#other-ways-to-install-rustup](https://forge.rust-lang.org/infra/other-installation-methods.html#other-ways-to-install-rustup)

Now check if everything was installed properly:

```bash
rustup -V  # The Rustup toolchain
rustc -V   # The Rust compiler
cargo -V   # Rust's package manager
```

Comparing Rust to Javascript - my first impression
--------------------------------------------------

I find Rust 'harder' than Javascript in a few ways. Whereas the latter's compiler makes almost no assumptions about how you write code, Rust comes with some opinionated patterns. And that's good, for the language promises a secure ecosystem to develop with.

> I felt like knowing Typescript made my experience a bit easier. If you're not ready for a hard cut, you should start off learning it first. You could start by [migrating a Javascript app to Typescript](https://blog.q-bit.me/a-step-by-step-guide-to-migrate-a-node-js-app-to-typescript/).

### A simple +1 - function - in Javascript and Rust  

Let's look at some code. Both of the following functions take in a user's input and add a `1`, then print it to the console.

In Javascript (Node.js), type the following:

```js
const readline = require('readline').createInterface({
  input: process.stdin,
  output: process.stdout,
});

function plusOne() {
  readline.question('Enter a number: ', (answer) => {
    console.log(`${answer} plus one is ` + (parseInt(answer) + 1));
    readline.close();
  });
}

plusOne();
```

The Rust implementation requires a bit more work:

```rs
use std::io;

pub fn plus_one() {
  let mut user_input = String::new();

  println!("Enter a number: ");
  io::stdin().read_line(&mut user_input).expect("Could not read user input");


  let user_input_san: &str = user_input.trim();
  let user_input_num: usize = user_input_san.parse().unwrap();
  let result: usize = user_input_num + 1;

  println!("{} plus 1 is {}", user_input_san, result)
}

fn main() {
  plus_one();
}
```

### A simple +1 - function - differences in the code

My first thoughts were: **This code looks super quirky.** Let's take a look at the commons and differences:

*   You bring modules into play with the keyword `use`
*   Variables are declared using the keyword `let` and are **immutable** by default. The keyword `mut` allows you to change their value
*   The `read_line` function returns a `Result` type instead of a value. It can be either the expected value OR an `Err` object
*   Instead of accessing a variable directly, you can access its reference by prefixing it with `&` . This mechanism is essential to Rust's ['borrow checking'](#distinction-3-memory) system
*   Typing is mandatory and there's an obligatory `main` function

And a few questions popped into my mind (hints included):

*   Why is there an exclamation mark behind `println!()`? [Hint](https://doc.rust-lang.org/book/ch19-06-macros.html?highlight=macros#macros)
*   What does `String::new()` mean? Is this a constructor? [Hint](https://doc.rust-lang.org/std/string/struct.String.html#method.new)
*   What in the world is the difference between `Number` and `usize`? [Hint](https://doc.rust-lang.org/std/primitive.usize.html#)

You need to consider more concepts in Rust than in Javascript development. Reading about string types alone made me feel naive at times. And I still feel clumsy writing the code.

Let's wrap up the first impression and get to the filet bits.

Distinction 1: The Rust compiler
--------------------------------

Javascript is executed in the browser or a Node.js runtime. The code you write will be compiled (= translated) while it runs. The resulting _machine code_ gives your computer instructions.

This type of compilation classifies Javascript as an **interpreted language**.

When you ship a Rust program, it'll be a single executable file. This is the _compiled machine code_ to be executed by the operating system. Software developed with a **compiled language** usually performs better. Since all variables, including their types, are known during compile time, the resulting software is less prone to errors as well.

> You can compile a single Rust file with `rustc <filename>`. It's recommended to use `[cargo](https://doc.rust-lang.org/book/ch01-03-hello-cargo.html?highlight=cargo#hello-cargo)` for bigger projects.

Distinction 2: Static types
---------------------------

The necessity to type variables felt unfamiliar when I did it for the first time. Let's look at the `plusOne` functions again for a second, especially at `console.log`, to see a possible implication.

```js
function plusOne() {
  readline.question('Enter a number: ', (answer) => {
    console.log(`${answer} plus one is ` + (parseInt(answer) + 1));
    readline.close();
  });
}
```

Can you guess what happens when we remove the outer braces from  `(parseInt(answer) + 1)`?

![](https://blog.q-bit.me/content/images/2022/06/image.png)

A real classic.

Errors like this are less likely to happen in Rust programs. Let's review `plus_one`, remove all types and the `.expect()` method from `io`:

```rust
use std::io;

pub fn plus_one() {
    let mut user_input = String::new();

    println!("Enter a number: ");
    io::stdin().read_line(&mut user_input);

    let user_input_san = user_input.trim();
    let user_input_num = user_input_san.parse().unwrap();
    let result = user_input_num + 1;

    println!("{} plus 1 is {}", user_input_san, result)
}

fn main() {
    plus_one();
}

```

Let's try to compile and see what happens.

![](https://blog.q-bit.me/content/images/2022/06/image-2.png)

Rust cannot infer the type of `user_input_num`. We must ensure type safety before the compiler can do its job.

> **Since Rust is a statically typed language, the compiler must know the type of all variables during compile time.**

Re-add the type annotation `usize` to `user_input_num` and compile again. You should see the following warning message:

![](https://blog.q-bit.me/content/images/2022/06/image-3.png)

The program still compiles, but it'll show you a possible bug in your code. You'll want to review this line and handle the possible edge case.

> If compilation and typing look way too alien, try and give Typescript a shot before you move ahead.

**Let's summarize.**

Not only does the compiler handle early error detection, but it also warns you where your code may be unambiguous. In situations where you rely on the fail-safety of your program, this behavior is indispensable.

Distinction 3: Memory
---------------------

When a program runs, its variables and functions are stored in the computer's RAM. It's a valuable resource and must be kept in check.

### Javascript uses garbage collection

A garbage collection takes care of freeing memory after a defined ruleset. It regularly checks whether a variable is still in use or not.

The following code gives a simple example of how Javascript manages memory:

```js
// Two objects are created and stored in memory.
// - One that's called `user`
// - One that's called `address`.
// Address is embedded into user.
let user = {
  firstName: "John",
  secondName: "Doe",
  address: {
    street: "123 Main Street",
    city: "Anytown",
  },
}

// We're creating a second reference here.
// `newUser` points to the same data in memory as `user`
let newUser = user;

// Let's also create a reference to the embeddedd address object.
let myAdress = user.address;

// By setting user to `null`, we remove the initial memory reference.
// It cannot be garbage collected yet because it's still referenced by newUser.
user = null;

// Removing the final reference of `newUser` will mark `user` as garbage.
// There's still a reference to `myAddress`, so it cannot be garbage collected yet.
newUser = null;

// By removing the final reference, `user` can be garbage collected.
myAdress = null;
```

### Rust uses a borrow checker

Garbage collection comes with a few tradeoffs:

*   Performance of software using GC is hard to predict
*   GC runs beside the actual program, adding more workload, which results in reduced performance
*   [Software using GC does not scale well](https://www.dynatrace.com/resources/ebooks/javabook/impact-of-garbage-collection-on-performance/)

In languages such as C, you must manually allocate and free memory. Unfortunately, such manual procedures are often fertile soil for bugs.

Rust uses a unique approach to solve this issue - introducing ownership and the borrow checker. They break down to three simple rules:

1.  Each value in Rust has a variable that's called the value's owner
2.  There can only be one owner at a time
3.  When the owner goes out of scope, the value will be dropped (and memory is freed)

Let's look at some code to highlight how it works. We'll use the example from before and implement a separate function to prompt for the user input.

```rust
use std::io;

pub fn prompt() -> String {
    let mut user_input = String::new();
    println!("Enter a number: ");
    io::stdin().read_line(&mut user_input).expect("Could not read user input");
    let prompt_value = user_input.trim().to_string();
    return prompt_value;
}

pub fn plus_one(user_input: String) {
    let user_input_num: usize = user_input.parse().unwrap();
    let result = user_input_num + 1;
    println!("{} plus 1 is {}", user_input, result)
}

fn main() {
    let input = prompt();
    plus_one(input);
    println!("The initial value was {}", input);
}
```

If you try and compile the above code, you'll run into the following error:

![](https://blog.q-bit.me/content/images/2022/06/image-4.png)

Let's see why this happens:

*   We create the variable `input`. It becomes the owner of the value returned by `prompt`.
*   By using it as an argument for `plus_one`, ownership is passed into the function's scope
*   `plus_one` runs and `input` goes out of scope. It's not available to be printed afterward

We can solve this problem in two ways:

1.  Print the value before executing `plus_one`.
2.  If we don't want to change the order, we can have `plus_one` borrow `input`

Borrowing means: We pass a reference to the variable instead of the variable as a whole. Like this, ownership remains constant and the compiler is happy.

> You can pass a variable's references by prefixing it with a `&`

```rust
use std::io;

pub fn prompt() -> String {
    let mut user_input = String::new();
    println!("Enter a number: ");
    io::stdin().read_line(&mut user_input).expect("Could not read user input");
    let prompt_value = user_input.trim().to_string();
    return prompt_value;
}

pub fn plus_one(user_input: &str) {
    let user_input_num: usize = user_input.parse().unwrap();
    let result = user_input_num + 1;
    println!("{} plus 1 is {}", user_input, result)
}

fn main() {
    let input = prompt();
    plus_one(&input);
    println!("The initial value was {}", input);
}

```

Moving ahead
------------

Let's recap.

You've read a fair share about Rust, Javascript, commonalities, and differences. Especially how Rust implements stricter rules to keep your code efficient.

These concepts look exotic at first. In the end, It's a tradeoff you make. Follow a few simple guidelines. In return, you receive a program that's blazing fast and memory-efficient.

As web developer, there's more to look forward to. [Web Assembly](https://developer.mozilla.org/en-US/docs/WebAssembly/Rust_to_wasm) allows you to integrate Rust code into Javascript projects, taking full advantage of Rust's safety and performance features. The project maintains an ambitious roadmap which you can find [here](https://webassembly.org/roadmap/).

### Further reading

*   Book: [The official Rust Programming Language book](https://doc.rust-lang.org/stable/book/)
*   Book: [Writing Rust in easy english](https://dhghomon.github.io/easy_rust/Chapter_0.html)
*   Video: [Traversy Media Rust Crash Course](https://www.youtube.com/watch?v=zF34dRivLOw)

> This post was originally published at https://blog.q-bit.me/get-started-with-rust-as-a-web-developer/
Thank you for reading. If you enjoyed this article, let's stay in touch on Twitter 🐤 [@qbitme](https://twitter.com/qbitme)