As Rust is a relatively new language compared to older languages such as C, Java, and even Javascript, it can be useful to have a rough overview of what makes Rust interesting, and how it differs from these languages (with a heavy focus on Typescript).
First, what is Rust ?
Rust is a modern systems programming language focused on performance, safety, and concurrency, without needing a garbage collector. It's often compared to C++, as it's where its advantages can shine the best (safety without sacrificing performance).
While its main domain is systems programming, it's also a very good language for writing backends. Of course, for most backends/APIs, having awesome performance isn't a hard requirement, and is often considered overkill compared to the complexity that is required to achieve it. But, one of the big advantages of Rust compared to other languages (C++, C, ...) is that it can achieve performance while providing a lot of safety mechanisms. This includes the borrow checker, but it also includes the explicit handling of undefined values (via Option<...>) and of errors (via Result<...>).
For backends, Rust is mainly compared to Golang, as both are performant compiled languages that can be used for backends. Rust is more oriented towards safety and performance, at the cost of increased complexity. On the other hand, Golang's main aim is simplicity, which makes it easy to learn, use, and understand. In short, both have their advantages and disadvantages.
let x = 5;
x = 6;let x = 5;
// x = 6; // ❌ Error: x is immutable
let mut x = 5;
x = 6; // ✅ Use `mut` to make variables mutablelet name: string = "Alice";let name: &str = "Alice"; // or just `let name = "Alice";` — Rust infers typesfunction getUser(): User | null {}fn get_user() -> Option<User> {
// Some(user) or None
}const role = "admin";
switch (role) {
case "admin":
// ...
}match role.as_str() {
"admin" => { /* ... */ }
_ => { /* ... */ }
}Rust tracks memory at compile time — no GC.
function takeName(name: string) {}
const myName = "Bob";
takeName(myName); // OK, string is copiedfn take_name(name: String) {}
let my_name = String::from("Bob");
take_name(my_name); // OK, but ownership moved
// my_name is no longer valid hereUse references to borrow data instead of moving it:
fn print_name(name: &String) {}
print_name(&my_name); // ✅ Borrowing, no moveRust doesn't have a built-in runtime like Node — you pick one (like tokio or async-std).
async function fetchData() {}async fn fetch_data() {}To run async functions, use .await inside an async runtime.
Rust enums are more powerful than TS unions.
type Result<T> = { ok: true; value: T } | { ok: false; error: string };enum Result<T, E> {
Ok(T),
Err(E),
}Like package.json, but for Rust.
[dependencies]
serde = "1.0"
tokio = { version = "1.0", features = ["full"] }cargo build— compilecargo run— run the appcargo test— run testscargo fmt— format codecargo clippy— linter
Rust’s compiler is very strict — but its error messages are famously helpful. Trust it!
Resources that can help when learning, or just developing in Rust