Neophyte Rustacean
One month with Rust
Me
- Professional Java/JVM developer
- Experienced in C
- Used C++, Scala, Python, assembly, ...
- Haskell for 18 months
What is Rust
- Strict
- Immutable* by default
- Lifecycle managed
- Low overhead
Goals of Rust
- Memory safe
- Zero-cost abstraction
- No data races
- Efficient
Aimed at replacing C/C++
Not aimed at being an alternative to Haskell
I would consider it a competitor to Go
Syntax
- Bullet One
- Bullet Two
- Bullet Three
Example from the book
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.ok()
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
Example from the book
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.ok()
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
- Prelude is minimal
- No IO
- Mostly lifecycle and memory management
- Option, Result, String, Vec
- 'std' create is small too
- controlled via RFCs
Example from the book
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.ok()
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
- Defaults to returning nothing, so side-effects are no delineated
Example from the book
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.ok()
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
- println! from the io module
- {} for string interpolation
- ! means this is a macro
- Compiler checks format string
Example from the book
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.ok()
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
- Bindings are immutable by default
- mut makes it mutable
- must be used when passing a mutable reference into a function too, as references are immutable by default
Example from the book
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.ok()
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
- String::new is a function associated with the String type
- io::stdin is a function in the io module
- foo.bar(...) a method call
- sort of syntactic sugar for bar(foo, ...) with expected resolution of bar
Example from the book
use std::io;
fn main() {
println!("Guess the number!");
println!("Please input your guess.");
let mut guess = String::new();
io::stdin().read_line(&mut guess)
.ok()
.expect("Failed to read line");
println!("You guessed: {}", guess);
}
- Rust has Option and Result types in the prelude
- Parameterised over their types
- io::Result<A> is an alias for std::Result<A, Error>
- ok is Result<A> -> Option<A>
- expect is Option<A> ->A
- panic! if it's None
Other syntax
and control flow
If
Works as you'd expect, no parentheses
If trailing else, expression with last value in each
If not, expression of type ()
if condition {
} else if {
} else {
}
For
For-each style for, using Iterator trait
for var in expression {
..
}
for i in 0..10 {
// goes from zero to nine!
}
While and loop
while statement and infinite loop, both unit type
while condition {
..
}
loop {
// prefer this to while "true"
}
Ownership, borrowing and references
Ownership
- Bindings own their data for their lifetime
- At most one owner for each piece of data
- Deallocated when it goes out of scope
- This is part of what prevents data races and provided memory safety
- One of the most interesting parts of Rust
Ownership - moving
let x = vec![0,1,2]
let y = x
println!("{}", x[0]) // will fail
Ownership of the vector was moved to y, so x is not longer a valid binding. rustc enforces this:
error: use of moved value: `x`
note: `x` moved here because it has type
`collections::vec::Vec<i32>`, which is moved by default
let y = x;
Ownership - moving
fun foo(v: Vec<i32>) {..}
let x = vec![0,1,2]
foo(x);
println!("{}", x[0]) // will fail
This will also fail, since ownership was moved to the function (as an argument)
Ownership - Copy
Some types implement the Copy trait
indicating that they can freely be copied without introducing data races
Numeric types (no pointers)
let v = 1;
let v2 = v;
println!("{}", v); // works
Ownership - borrowing
What if we want to pass data to a function and then use it afterwards? The function could return it, but that would be very tedious. Instead we let the function borrow our data (implemented as a pointer).
fn foo(v: &Vec<i32>) {..}
let v = vec![0,1,2];
foo(v)
println!("{}", v[0]); // works
Ownership - borrowing
References are immutable by default, can't change data
fn foo(v: &Vec<i32>) {v.push(9);}
let v = vec![0,1,2];
foo(v)
println!("{}", v[0]);
error: cannot borrow immutable borrowed content `*v` as mutable v.push(9);
Ownership - borrowing
You can use a mutable reference, on both the argument and reference
fn foo(v: &mut Vec<i32>) {v.push(9);}
let v = vec![0,1,2];
foo(&mut v)
println!("{}", v[0]); // works
Ownership - borrowing
You cannot have mutable and immutable references at the same time
fn foo(v: &Vec<i32>) {..}
let v = vec![0,1,2];
let v2 = &mut v2
foo(v); // v2 is a mutable borrow,
//so can't pass immutable reference to function
Ownership - borrowing
If you limit the scope of the mutable borrow, it no longer exists at the point we want an immutable borrow.
fn foo(v: &Vec<i32>) {..}
let v = vec![0,1,2];
{
let v2 = &mut v2
}
foo(v); // works
Ownership - rules
You can have exactly one mutable reference,
or any number if immutable references,
but not both
Traits
Typeclasses for Rust
Traits
Rust has traits, which are basically typeclasses
x
neophyte-rustacean
By doctau
neophyte-rustacean
- 992