Originally published on php2rust.dev — part of the PHP to Rust course. View this lesson on the original site.

Overall, Rust is versatile and excels in many areas, from system-level programming to web development. If you've spent years building backends, APIs, or WordPress/Symfony sites in PHP, Rust opens doors to the same kind of work (web services, CLIs, APIs) with different guarantees: no garbage collector, strong static typing, and a compiler that catches whole classes of bugs before you run a line of code. It's a valuable addition to any developer's skill set, and your PHP experience will carry over more than you might expect.

Rust is valued for its high performance, security, and ability to handle concurrency. In PHP you're used to each request running in isolation; Rust is often used for long-running services where many tasks run at once, with the compiler enforcing thread safety. These qualities matter when software must be fast, scalable, and resistant to vulnerabilities, whether that's a high-traffic API or a CLI tool you ship to production.

The main shift for PHP developers is Rust's approach to memory: no garbage collector, and the compiler tracks ownership and borrowing. Once you get used to it, you'll write code that is both safe and fast. Unlike C/C++ (manual memory) or PHP (GC), Rust gives you a different but learnable mindset, and many concepts (structs, enums, pattern matching, traits) will feel like a sharper version of what you already know.

Let's break it down:

General-purpose

Rust is versatile. It's used for systems programming, web backends, CLIs, and even frontends via WebAssembly, similar breadth to where you might use PHP. In PHP you might build a Symfony or WordPress site; in Rust you'd use frameworks like Axum or Actix for backends, or compile to Wasm for the browser. Same idea: pick the right tool for the job.

Performance, type safety, and concurrency

Rust is fast (comparable to C), statically typed, and built for safe concurrency. In PHP you rely on the runtime; in Rust the compiler enforces rules up front.

Types, PHP 8+ has union types and better typing. Rust goes further: every variable has a type, and the compiler catches mismatches before you run. The key difference is when the check happens.

Runtime vs compile-time, In PHP, type checks happen at runtime: the code runs, and when execution reaches a bad call, you get a TypeError (or coercion, if types are loosened). You might not hit that code path during testing, a rare form submission, an edge case, so the bug can slip into production. In Rust, type checks happen at compile time: the compiler builds your program and refuses to produce a binary if there's a type mismatch. You can't ship broken code. The feedback loop is immediate: fix the error, then run. No surprises at 3 a.m. when that edge case finally fires.

// PHP, runtime checks, or none if you skip types
function add(int $a, int $b): int {
    return $a + $b;
}
add("5", 3);  // TypeError at runtime, or coercion if types are relaxed
// Rust, compile-time check
fn add(a: i32, b: i32) -> i32 {
    a + b
}
add("5", 3);  // Won't compile: type mismatch. Fix it before you can run.

Concurrency, In PHP you use workers, queues, or Fibers. Rust has threads and async/await with strict rules so you can't accidentally share data unsafely. The compiler prevents data races at compile time.

Memory safety without a garbage collector

PHP has a garbage collector. When you're done with a variable, the runtime frees the memory. Rust has no GC, instead it uses ownership and borrowing. Each value has one owner; when the owner goes out of scope, the value is dropped. No manual free, no use-after-free.

// PHP, GC manages everything
$data = fetchLargeDataset();
process($data);
// $data sticks around until GC runs; you don't think about it
// Rust, ownership decides when memory is freed
let data = fetch_large_dataset();
process(data);
// data is dropped here when it goes out of scope
// The compiler enforces that you don't use data after it's moved

You'll learn ownership and borrowing step by step. The mental model is different, but it becomes natural with practice.

The borrow checker

The borrow checker enforces Rust's ownership rules. It prevents use-after-free, double-free, and data races. It can feel strict at first, you'll get compile errors that seem fussy, but it catches real bugs before your code ever runs.

In PHP you might pass an array by reference and mutate it; if two parts of the code hold references, you can get unexpected behaviour. Rust's rules (one mutable reference, or many immutable ones, but never both) make that class of bug impossible.

PHP, references can coexist and mutate, which can lead to subtle bugs:

<?php
$arr = [1, 2, 3];
$ref1 = &$arr;
$ref2 = &$arr;
$ref2[] = 4;  // Both refs point to same array; behaviour can be surprising

Rust, the compiler rejects conflicting borrows:

let mut v = vec![1, 2, 3];
let r1 = &v;      // immutable borrow
let r2 = &mut v;  // ERROR: can't have mutable borrow while r1 exists

In PHP, similar patterns can lead to subtle bugs; in Rust, the compiler stops you.

What's next

Let's look at the key features.

From PHP to Rust — Getting Started with Rust