Rust @ Functional Vilnius
@nercury
Rust?
Safe
Efficient
Concurrent
SERVO
+
(think C++)
(think taaaabs)
(no memory mis-use)
Rust's path?
Control vs. Safety
CONTROL
SAFETY
C
C++
Go
Java
C#
Python
PHP
Ruby
Haskell
Rust
CONTROL
SAFETY
YOUR
INTENT
The Secret Sauce
Ownership and Borrowing
Explaining your intent at compile-time
Hello World
fn main() {
println!("Hello world!");
}
Functions
fn sum(a: i32, b: i32) -> i32 {
a + b
}
fn main() {
let answer = sum(2, 3);
println!("Result is {:?}", answer);
}
Machine Types
- u8, u16, u32, u64
- i8, i16, i32, i64
- f32, f64
- usize, isize
Primitive Types
- Tuples: (i32, u64)
- Unit type: ()
- Booleans: bool
Structs
struct Point {
x: i32,
y: i32,
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("Point x: {:?}, y: {:?}", p.x, p.y);
}
fn get_area(point: Point) -> i32 {
point.x * point.y
}
fn main() {
let p = Point { x: 3, y: 4 };
let area = get_area(p);
println!(
"Point x: {:?}, y: {:?}, area: {:?}",
p.x, p.y, area
);
}
fn get_area(point: Point) -> i32 {
point.x * point.y
}
fn main() {
let p = Point { x: 3, y: 4 };
let area = get_area(p);
println!(
"Area: {:?}",
area
);
}
OK!
struct Point {
x: i32,
y: i32,
}
impl Point {
fn to_area(self) -> i32 {
self.x * self.y
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!(
"Area: {:?}",
p.to_area()
);
}
Impls
Ownership System
Ownership & Mutability
RAII
use std::net::TcpStream;
fn main () {
let _stream =
TcpStream::connect("127.0.0.1:8080").unwrap();
//...
} // the stream is closed here
Heap
fn main() {
let p = Point { x: 3, y: 4 };
let b = Box::new(p);
println!("Point x: {:?}, y: {:?}", b.x, b.y);
}
String
fn main() {
let mut s = String::new();
s.push_str("World!");
println!("Hello {}", s);
}
Vec
fn main() {
let mut items = vec!["Hello"];
items.push("World!");
println!("{}", items.connect(" "));
}
Closures
fn main() {
let double_me = |x| x + x;
let double_us = |x, y| (double_me(x), double_me(y));
println!("{:?}", double_us(4, 30));
let double_small_number =
|x| if x > 100 {
x
} else {
x * 2
};
println!("small doubled: {:?}", double_small_number(42));
println!("large doubled: {:?}", double_small_number(1337));
}
1
2
3
Thread
use std::thread;
fn main() {
let a = 2;
let t = thread::spawn(
move || a * 2
);
println!("{:?}", t.join()); // Ok(4)
}
Threads
use std::thread;
fn main() {
let items = vec!["Functional", "Vilnius", "Meetup"];
let mut threads = Vec::new();
for item in items {
threads.push(
thread::spawn(
move || item.len()
)
);
}
let mut results = Vec::new();
for thread in threads {
results.push(thread.join());
}
println!("{:?}", results); // [Ok(10), Ok(7), Ok(6)]
}
Functional -ish Threads
use std::thread;
fn main() {
let items = vec!["Functional", "Vilnius", "Meetup"];
let threads: Vec<_> = items.into_iter()
.map(
|item| thread::spawn(
move || item.len()
)
)
.collect();
let results: Vec<_> = threads.into_iter()
.map(
|thread| thread.join()
)
.collect();
println!("{:?}", results); // [Ok(10), Ok(7), Ok(6)]
}
Channels
use std::thread;
use std::sync::mpsc::channel;
fn main() {
let (tx, rx) = channel();
for i in 0..3 {
let tx = tx.clone();
thread::spawn(
move || tx.send(i).unwrap()
);
}
println!("{:?}", rx.recv()); // Ok(2)
println!("{:?}", rx.recv()); // Ok(0)
println!("{:?}", rx.recv()); // Ok(1)
}
Borrowing System
Immutable borrow:
let a = 42;
let ref = &a;
Mutable borrow:
let mut a = 42;
let ref = &mut a;
Only Aliasing
Only Mutation
Iterator Invalidation
fn main() {
let mut items = vec![1, 2, 3];
for item in items { // error
items.clear();
}
}
<anon>:5:9: 5:14 error: use of moved value: `items`
<anon>:5 items.clear();
^~~~~
<anon>:4:17: 4:22 note: `items` moved here because it has type
`collections::vec::Vec<i32>`, which is non-copyable
<anon>:4 for item in items {
^~~~~
fn main() {
let mut items = vec![1, 2, 3];
for item in &items { // error
items.clear();
}
}
<anon>:5:9: 5:14 error: cannot borrow `items` as mutable because it is also
borrowed as immutable
<anon>:5 items.clear();
^~~~~
<anon>:4:18: 4:23 note: previous borrow of `items` occurs here; the immutable
borrow prevents subsequent moves or mutable borrows
of `items` until the borrow ends
<anon>:4 for item in &items {
^~~~~
fn main() {
let mut items = vec![1, 2, 3];
for item in &mut items { // error
items.clear();
}
}
<anon>:5:9: 5:14 error: cannot borrow `items` as mutable more than
once at a time
<anon>:5 items.clear();
^~~~~
<anon>:4:22: 4:27 note: previous borrow of `items` occurs here; the
mutable borrow prevents subsequent moves, borrows,
or modification of `items` until the borrow ends
<anon>:4 for item in &mut items {
^~~~~
impl Vec {
fn clear(&mut self) {
...
}
}
Profit!
- No data races
- No dangling pointers
- No segfaults
- No read of uninitialized memory
- Non UTF-8 sequences in String
- No mutating immutable data
- No Garbage Collector necessary
- No runtime
- Compile-time guarantees
However...
- Can still deadlock
- Object cycles can still memory leak
- Integer overflow is user error
- Can exit without calling destructor
- There is a little keyword called unsafe
System Calls
#[link(name = "cpuid")]
extern {
pub fn cpuid_get_raw_data(raw: *mut cpu_raw_data_t) -> c_int;
}
pub fn identify() -> Result<CpuInfo, String> {
let mut raw: ffi::cpu_raw_data_t = Default::default();
let raw_result = unsafe {
ffi::cpuid_get_raw_data(&mut raw)
};
if raw_result != 0 {
return Err(error());
}
...
Ok(CpuInfo {
num_cores: data.num_cores as int,
num_logical_cpus: data.num_logical_cpus as int
})
}
struct Point {
x: i32,
y: i32,
}
impl Point {
fn to_area(self) -> i32 {
self.x * self.y
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!(
"Area: {:?}",
p.to_area()
);
}
Back to the point...
struct Point {
x: i32,
y: i32,
}
impl Point {
fn get_area(&self) -> i32 {
self.x * self.y
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!(
"Area: {:?}",
p.get_area()
);
}
struct Point {
x: i32,
y: i32,
}
impl Point {
fn offset_x(&mut self, val: i32) {
self.x += val;
}
}
fn main() {
let mut p = Point { x: 3, y: 4 };
p.offset_x(2);
println!("X: {:?}", p.x);
}
struct Rect {
x: f32,
y: f32,
}
trait GetArea {
fn get_area(&self) -> f32;
}
impl GetArea for Rect {
fn get_area(&self) -> f32 {
self.x * self.y
}
}
fn main() {
let r = Rect { x: 3.0, y: 4.0 };
println!("Area: {:?}", r.get_area());
}
Traits
struct Circle {
r: f32,
}
impl GetArea for Circle {
fn get_area(&self) -> f32 {
PI * self.r * self.r
}
}
impl Circle {
fn larger_than(&self, other: &Rect) -> bool {
self.get_area() > other.get_area()
}
}
fn main() {
let r = Rect { x: 3.0, y: 4.0 };
let c = Circle { r: 3.0 };
if c.larger_than(&r) {
println!("Circle is larger!");
} else {
println!("Rectangle is larger!");
}
}
struct Circle {
r: f32,
}
impl GetArea for Circle {
fn get_area(&self) -> f32 {
PI * self.r * self.r
}
}
impl Circle {
fn larger_than<T: GetArea>(&self, other: &T) -> bool {
self.get_area() > other.get_area()
}
}
fn main() {
let r = Rect { x: 3.0, y: 4.0 };
let c = Circle { r: 3.0 };
if c.larger_than(&r) {
println!("Circle is larger!");
} else {
println!("Rectangle is larger!");
}
}
trait GetArea {
fn get_area(&self) -> f32;
fn larger_than<T: GetArea>(&self, other: &T) -> bool {
self.get_area() > other.get_area()
}
}
// struct Rect { ...
// impl GetArea for Rect { ...
// struct Circle { ...
// impl GetArea for Circle { ...
fn main() {
let r = Rect { x: 3.0, y: 4.0 };
let c = Circle { r: 3.0 };
if c.larger_than(&r) {
println!("Circle is larger!");
} else {
println!("Rectangle is larger!");
}
}
Operator Traits
use std::ops::{ Add, Sub, Neg };
impl Add for Point {
type Output = Point;
fn add(self, other: Point) -> Point {
Point { x: self.x + other.x, y: self.y + other.y }
}
}
impl Neg for Point {
type Output = Point;
fn neg(self) -> Point {
Point { x: -self.x, y: -self.y }
}
}
impl Sub for Point {
type Output = Point;
fn sub(self, other: Point) -> Point {
self + (-other)
}
}
1
2
3
Traits Everywhere
- Arithmetic (Add, Sub, Neg, ..)
- Iterators, Indexing, Range
- Parsing (FromStr, ..)
- Formatting (ToString, Debug, Display, ..)
- Closures are traits (Fn, FnMut, FnOnce)
- IO (Cursor, Read, Write, ..)
- Memory Management (Deref, Cow, Borrow, ..)
Enum Types
enum Color {
Rgb((u8, u8, u8)),
Undefined,
}
fn main() {
let color = Color::Rgb((33, 44, 55));
match color {
Color::Rgb((r, g, b)) => {
println!("{}, {}, {}", r, g, b);
},
_ => {
println!("Nothing!");
},
}
}
Result Types
enum Option<T> {
Some(T),
None,
}
enum Result<T, E> {
Ok(T),
Err(E)
}
fn main() {
match maybe_result = computation() {
Ok(result) => println!("{:?}", result),
Err(e) => println!("Error! {:?}", e),
}
}
use std::fs::File;
use std::io::{ BufReader, BufRead };
fn main() {
if let Ok(file) = File::open("foo.txt") {
let sum = BufReader::new(file)
.lines()
.filter_map(|maybe_line| maybe_line.ok())
.filter_map(|line| line.parse().ok())
.fold(0, |acc, i: i32| acc + i);
println!("Sum is {:?}.", sum);
}
}
Reading File
let action = match time_left() {
Some(time) => time.to_demo(),
_ => None,
}
Rust tools
- Package manager, cargo, is awesome!
- Find rust libraries (called crates) on crates.io;
- Built-in tests!
- Built-in documentation generator!
- Your favorite editor has a plugin that provides at least the syntax highlighting, and at most - code completion and debugging.
Lithuanian Rust Developers @ LinkedIn
rust-lang.org
play.rust-lang.org
crates.io
Questions?
@nercury, @fpvilnius
Rust
By nercury
Rust
Introduction to Rust programming language
- 2,977