Rust is a new programming language from Mozilla that provides memory safety without garbage collection and performance matching C. It uses ownership and borrowing to prevent common memory issues like null pointer dereferences and double frees. Rust also supports algebraic data types, option types, pattern matching, and macros to enable safe concurrency.
2. What is it?
A new programming language from Mozilla.
Memory safety without garbage collection.
Performance matching that of C.
Safe concurrency.
3. Without further ado
fn main() {
println!("Hello, world!");
}
$ rustc hello.rs
$ ./hello
Hello, world!
4. Memory Safety
Rust constrains where and when you can dereference pointers.
The compiler can prove where to insert calls to free().
No dereferencing NULL pointers, no double free() errors, or buffer
overflows.
5. Example: Double Free
In C, this cuases a double free error:
#include <stdlib.h>
int main() {
int* ptr = malloc(sizeof(int));
int* ref = ptr;
free(ptr);
free(ref);
}
This is a contrived example, but double free errors happen often,
e.g. freeing a recursive data structure.
6. Example: Double Free
In Rust:
fn main() {
let ptr: Box<i32> = box 10;
let r = ptr;
}
No explicit free(), the compiler figures out where to free the memory.
7. Ownership
Note, however, we can’t use both pointers at the same time. The
following causes an error:
fn main() {
let ptr: Box<i32> = box 10;
let r = ptr;
*ptr = 11;
}
Rust pointers are not raw numbers like in C, rather, they are like
std::unique_ptr or std::auto_ptr.
When pointer r is assigned the value of pointer ptr, ptr becomes invalid
for the rest of the lifetime of r.
8. Algebraic Data Types
ADTs are like classes, but in reverse.
Instead of having a base class, and classes that inherit from that one, etc.,
you have a single type that can be any one of different variants.
9. Algebraic Data Types
Instead of this:
class Geometry {};
class Square : Geometry {
double side;
public:
Square(double s): side(s) {};
};
class Rectangle : Geometry {
double length, width;
public:
Rectangle(double l, double w): length(l), width(w) {};
};
int main() {
Square sq = Square(10.0);
Rectangle rect = Rectangle(1.2, 3.2);
}
10. Algebraic Data Types
You have this:
enum Geometry {
Square(f64),
Rectangle(f64, f64)
}
fn main() {
let sq = Geometry::Square(10.0);
let rect = Geometry::Rectangle(1.2, 3.2);
}
11. Option Types
Defined like this:
enum Option<T> {
Some(T),
None,
}
Used like this:
fn safe_division(dividend: int, divisor: int) -> Option<int> {
if divisor == 0 {
None
} else {
Some(dividend / divisor)
}
}
Option types are essentially like having a NULL-able value, only unlike most
type systems, you get to choose which types are nullable.
13. Simple Matching
You can match values
fn print_int(x: i64) {
match x {
1 => println!("one");
2 => println!("two");
_ => println!("Not implemented :C");
}
}
And ranges and expressions:
match num {
1 | 2 => ...;
3 ... 5 => ...;
_ => ...;
}
14. More Complex Matching
And other constructs, like the Option type:
let div = safe_division(1, 0);
match div {
Some(q) => ...; // Do something with the quotient
None => ...; // Divisor was 0
}
15. Macros
Macros provide source-to-source transformation.
You define a macro to match a particular pattern of source code, and
transform it into another.
Patterns can include variables.
16. Example: Macros
For example, let’s make a macro for constructing linked lists.
You have a list like this:
enum List {
Cons(i64, Box<List>),
Nil
}
So we write this recursive macro:
macro_rules! make_list (
() => {
List::Nil
};
($first:expr $(, $rest:expr)*) => {
List::Cons($first, box make_list!($($rest),*));
};
)
17. Example Source
So now, this:
let list = make_list!(1, 2, 3);
Expands to this:
let list =
List::Cons(1,
box() List::Cons(2,
box() List::Cons(3,
box() List::Nil)));