Rust
Rust began as a personal project of Graydon Hoare in 2006, was sponsored by Mozilla, and reached version 1.0 in 2015. It aims at the speed and control of C and C++ while making memory errors impossible in safe code, with no garbage collector.
Paradigm: Systems Multi-Paradigm Languages
What Makes Rust Special
- Ownership. Every value has one owner. When the owner goes out of scope the value is freed, and assigning it elsewhere moves it. The compiler checks this, so use-after-free and double-free cannot happen.
- Borrowing and lifetimes. You can lend a value as
&T(shared, read-only) or&mut T(exclusive, writable), never both at once. This rule also prevents data races between threads. - Traits instead of inheritance. A trait describes behaviour a type can have. Types implement traits, and generic code asks for them as bounds. There are no classes and no inheritance of data.
- Enums and pattern matching. An
enumcan carry data in each variant (an algebraic data type), andmatchmust cover every case.OptionandResultreplace null and exceptions. - Functional idioms. Closures and iterator chains (
map,filter,sum) compile down to loops with no overhead. Values are immutable unless declaredmut. - Fearless concurrency. The same ownership rules are checked across threads, so many races become compile errors.
Example: Enums, traits, iterators and ownership
use std::f64::consts::PI;
#[derive(Debug)]
enum Shape {
Circle(f64),
Rect { w: f64, h: f64 },
}
trait Area {
fn area(&self) -> f64;
}
impl Area for Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle(r) => PI * r * r,
Shape::Rect { w, h } => w * h,
}
}
}
fn main() {
let shapes = vec![Shape::Circle(1.0), Shape::Rect { w: 2.0, h: 3.0 }];
// functional: an iterator chain
let total: f64 = shapes.iter().map(|s| s.area()).sum();
println!("{:.2}", total); // 9.14
// Option instead of null
match shapes.iter().find(|s| s.area() > 5.0) {
Some(s) => println!("{:?}", s), // Rect { w: 2.0, h: 3.0 }
None => println!("none"),
}
// ownership: a move
let name = String::from("rust");
let moved = name;
// println!("{}", name); // error: borrow of moved value
println!("{}", moved); // rust
}
How It Works
Shapeis an enum whose variants hold different data.matchinareahandles each variant, and omitting one would be a compile error.Areais a trait, andimpl Area for Shapeattaches the behaviour. Any generic function can now acceptT: Area.shapes.iter().map(...).sum()is an iterator pipeline. It is compiled into a plain loop with no allocation.findreturnsOption, so the code must handle the case where nothing matches.let moved = namemoves the string. Usingnameafterwards is rejected by the compiler, which is how Rust avoids double frees.
History and Where It Is Used
Rust is used for browsers (parts of Firefox), operating system components, command-line tools, embedded systems, WebAssembly, network services and cryptography. Support for Rust drivers was added to the Linux kernel in version 6.1. The toolchain includes cargo, a build tool and package manager, and the crates.io registry. The main cost is a steeper learning curve while you learn to satisfy the borrow checker. See also the full Rust roadmap on this site.