fn main() {
println!("Hello World!"); (1)
}
Basics of Rust
Traits
Generics
Error handling
Closures
Iterators
Designed by Mozilla, released in 2010
Multi-paradigm (imperative, functional, concurrent)
Static and strongly typed (with type inference)
Safe
Low-level
Automatic memory management (without garbage collector)
Servo web engine
Zero-cost abstractions
Concurrent programming
Safe:
No null pointers
No use after free
No use before initialization
Immutable variables by default
No memory leaks
fn main() {
println!("Hello World!"); (1)
}
println!
is a macro.
let number: i32 = 42; (1)
let number = 42; (2)
let mut number = 24; (3)
32-bit signed integer variable
specifying the type is optional thanks to type inference
mutable variable
struct Person {
firstname: String,
lastname: String,
age: u8, (1)
}
impl Person {
fn full_name(&self) -> String { (2) (3)
format!("{} {}", self.firstname, self.lastname)
}
}
8-bit unsigned integer
self
is the current instance
&
specify that self is passed by reference
let lastname = "Hoare".to_string();
let firstname = "Graydon".to_string();
let person = Person {
age: 42,
lastname: lastname,
firstname,
};
let full_name = person.full_name();
#[derive(Clone)]
struct Person {
firstname: String,
lastname: String,
age: u8,
}
match number {
1 => println!("one"),
2 => println!("two"),
3 | 4 => println!("three or four"),
5 ... 10 => println!("between 5 and 10"),
_ => println!("other"),
}
enum List {
Empty,
Cons(i32, Box<List>),
}
fn sum_list(list: &List) -> i32 {
match *list {
List::Empty => 0,
List::Cons(element, ref rest) =>
element + sum_list(rest),
}
}
if
)fn is_empty(list: &List) -> bool {
if let List::Empty = *list {
true
}
else {
false
}
}
for
loop)let nums = 10..20;
for (index, num) in nums.enumerate() {
println!("Num {} at index {}", num, index);
}
let nums = 10..20;
for tuple in nums.enumerate() {
println!("Num {} at index {}", tuple.1, tuple.0);
}
let text =
if number > 42 { (1)
"greater than 42" (2)
}
else {
"lesser than or equal to 42"
};
if
is an expression
no semicolon
fn max(number1: i32, number2: i32) -> i32 {
if number1 > number2 {
number1
}
else {
number2
}
}
Ad-hoc polymorphism (function/operator overloading)
A set of methods and types that can be defined for a type
Useful for generic functions
Very similar to typeclasses in Haskell
Similar to interfaces in OO languages
Display
Debug
Eq
Ord
Add
Sub
Mul
#[derive(Debug)]
enum List {
Empty,
Cons(Point, Box<List>),
}
use List::{Cons, Empty};
let p1 = Point::new(1, 2);
let p2 = Point::new(2, 3);
let p3 = Point::new(3, 4);
let p4 = Point::new(4, 5);
println!("{:?}", Cons(p1, Box::new(Cons(p2, Box::new(Cons(p3,
Box::new(Cons(p4, Box::new(Empty)))))))));
Cons(Point { x: 1, y: 2 }, Cons(Point { x: 2, y: 3 }, Cons(Point { x: 3, y: 4 }, Cons(Point { x: 4, y: 5 }, Empty))))
println!("{:#?}", Cons(p1, Box::new(Cons(p2, Box::new(Cons(p3, (1)
Box::new(Cons(p4, Box::new(Empty)))))))));
Usage of {:#?}
instead of {:?}
Cons(
Point {
x: 1,
y: 2
},
Cons(
Point {
x: 2,
y: 3
},
Cons(
Point {
x: 3,
y: 4
},
Cons(
Point {
x: 4,
y: 5
},
Empty
)
)
)
)
The trait must be in scope to use its methods
Either the type or the trait must be defined in the same crate as the implementation
trait Read {
fn read(string: &str) -> Self;
}
impl Read for i32 {
fn read(string: &str) -> Self {
string.parse().unwrap()
}
}
trait Equal {
fn equal(&self, other: &Self) -> bool;
fn not_equal(&self, other: &Self) -> bool { (1)
!self.equal(other)
}
}
trait Ordered : Equal { (2)
fn lesser_than_or_equal(&self, other: &Self) -> bool;
fn lesser_than(&self, other: &Self) -> bool {
!self.greater_than_or_equal(other)
}
fn greater_than(&self, other: &Self) -> bool {
!self.lesser_than_or_equal(other)
}
fn greater_than_or_equal(&self, other: &Self) -> bool {
self.greater_than(other) || self.equal(other)
}
}
Default implementation of the method
The types implementing Ordered
must also implement Equal
Parametric polymorphism (generic function or type)
Way to avoid code duplication
Static dispatch when using trait bounds
fn min<T>(a: T, b: T) -> T {
if a < b {
a
}
else {
b
}
}
error[E0369]: binary operation `<` cannot be applied to type `T`
--> src/main.rs:227:8
|
227 | if a < b {
| ^^^^^
|
= note: an implementation of `std::cmp::PartialOrd` might be missing for `T`
fn min<T: PartialOrd>(a: T, b: T) -> T { (1)
if a < b {
a
}
else {
b
}
}
println!("{}", min(24, 42));
The type T
must implement PartialOrd
enum List<T> {
Empty,
Cons(T, Box<List<T>>),
}
enum List<T: Eq> {
Empty,
Cons(T, Box<List<T>>),
}
enum List<T>
where T: Eq
{
Empty,
Cons(T, Box<List<T>>),
}
let list = Cons::<u8>(42, Box::new(Empty::<u8>)); (1)
let list: List<u8> = Cons(42, Box::new(Empty));
let tail = Empty; (2)
let list = Cons(42u8, Box::new(tail));
This looks like a fish ::<>
The compiler knows tail
has type List<u8>
because it is used with an u8
afterwards
Option
Result
Macros
No null
No exceptions
Encode the absence of value in the type itself
Cannot access the value directly
Like the Maybe
type in Haskell
enum Option<T> {
Some(T),
None,
}
let vector = vec![1, 2, 3];
let value =
match vector.first() {
Some(value) => value + 1,
None => 0,
};
optional.unwrap_or(0)
optional.unwrap_or_default()
optional.map(i32::abs)
optional.map_or(0, i32::abs)
optional.and_then(parse_int)
optional.cloned()
Encode the error in the type
#[must_use]
Like the Either
type in Haskell
Methods similar to Option
enum Result<T, E> {
Ok(T),
Err(E),
}
match "42".parse::<i32>() {
Ok(value) => println!("{}", value),
Err(error) => println!("Error: {}", error),
}
?
operatorGet the result or return the error if any
Shortcut for pattern matching
Useful when you have multiple Result
s, especially of different types.
fn sum_string(str1: &str, str2: &str) -> Result<i32, ParseIntError> {
let num1: i32 = str1.parse()?;
let num2: i32 = str2.parse()?;
Ok(num1 + num2)
}
fn sum_string(str1: &str, str2: &str) -> Result<i32, ParseIntError> {
let num1: i32 =
match str1.parse() {
Ok(num) => num,
Err(error) => return Err(error),
};
let num2: i32 =
match str2.parse() {
Ok(num) => num,
Err(error) => return Err(error),
};
Ok(num1 + num2)
}
Panic the thread
Unwind the stack (default)
Abort
panic!("don't panic");
let string = "rebar";
if string.parse::<i32>().is_err() {
panic!("Cannot parse: {}", string);
}
unimplemented!()
unreachable!()
assert!()
assert_eq!()
assert_ne!()
// (and their `debug_*` variants)
RUST_BACKTRACE=1
)thread 'main' panicked at 'Cannot parse: rebar', src/main.rs:136:8
note: Run with `RUST_BACKTRACE=1` for a backtrace.
# with RUST_BACKTRACE=1
thread 'main' panicked at 'Cannot parse: rebar', src/main.rs:136:8
stack backtrace:
# […]
7: functional_rust::main
at src/main.rs:136
8: __rust_maybe_catch_panic
at /checkout/src/libpanic_unwind/lib.rs:98
9: std::rt::lang_start
at /checkout/src/libstd/panicking.rs:459
at /checkout/src/libstd/panic.rs:361
at /checkout/src/libstd/rt.rs:61
10: main
# […]
Function without name
Type inference
Can have an environment
Allocated on the stack by default
let inc_opt = optional.map(|num| num + 1); (1)
fn sum_string(str1: &str, str2: &str) -> Result<i32, ParseIntError> {
str1.parse()
.and_then(|num1: i32| -> Result<i32, _> { (2) (3)
let num2: i32 = str2.parse()?;
Ok(num1 + num2)
})
}
Closure incrementing a number
Argument and return types
Block of code
trait Functor {
fn fmap<F, O>(&self, func: F) -> O
where F: Fn(&Self) -> O; (1)
}
let two = 2;
println!("{:?}", 40.fmap(|num| (num + two).to_string())); (2)
Generic parameter that must implement this Fn
trait
Capture the variable two
fn compose<A, B, C, F, G>(f: F, g: G) -> impl Fn(A) -> C (1)
where F: Fn(A) -> B,
G: Fn(B) -> C,
{
move |value| g(f(value)) (2)
}
This returns the closure without allocating it on the heap.
The keyword move
indicates that the environment is moved into the closure
High-level, but zero cost
Adapters, like filter
and map
Used in for
loops
Immutability
Iterator
trait
let string = b"RustConf, August 18-19 | Portland";
let string: String = (1) (2)
string.iter() (3)
.map(|byte| byte.to_ascii_uppercase())
.take_while(|&byte| byte != b'|') (4)
.map(|byte| byte as char)
.filter(|&ch| ch.is_alphanumeric())
.collect();
Immutable variable
Type annotation for collect()
Get an iterator
Pattern match against &byte
for
loopfor (&ch, index) in string.iter().zip(0..).skip(5) { (1)
if index < 20 {
print!("{}", ch as char);
}
}
0..
is an infinite Range
ISBN: 9781788390637