Upload
angelo-corsaro
View
215
Download
1
Embed Size (px)
Citation preview
Cop
yrig
ht P
rism
Tech
, 201
7
AngeloCorsaro,PhDCTO,ADLINKTech.Inc.Co-Chair,[email protected]
RUSTingPartially Ordered Rust Programming Ruminations
Cop
yrig
ht P
rism
Tech
, 201
7
RUSTing is not a tutorial on the Rust programming language.
I decided to create the RUSTing series as a way to document and share programming idioms and techniques.
From time to time I’ll draw parallels with Haskell and Scala, having some familiarity with one of them is useful but not indispensable.
Prologue
Cop
yrig
ht P
rism
Tech
, 201
7Rust is a system programming language that provides zero cost high-level abstractions and language-enforced memory and concurrency safety.
What is Rust?
Cop
yrig
ht P
rism
Tech
, 201
7
Installing Rust is simple, just do*:
Getting Started
$ curl https://sh.rustup.rs -sSf | sh
I suggest you also install the documentation locally:
$ rustup component add rust-docs
Open the doc with:
$ rustup doc
Cop
yrig
ht P
rism
Tech
, 201
7
Working with Monads…
Cop
yrig
ht P
rism
Tech
, 201
7
The first monad one typically encounter is a List, but the second one is usually the Maybe Monad
Like Scala, and differently from Haskell, Rust has named the Monad used to model the “potential” presence of a value as std::option::Option
Maybe is an Option
Cop
yrig
ht P
rism
Tech
, 201
7
The Option Monad is an algebraic data type with two variants, Some(T) and None
The derive directives injects a series of traits
The rest of the monad is implemented in the impl clause (see here)
The Option Monad
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub enum Option<T> {
None,
Some(T), }
Cop
yrig
ht P
rism
Tech
, 201
7
Depending on your background you may be familiar with at least three ways of working with Option, and with Monad in generals
- map/flatMap - map :: (a -> b) -> M a -> M b - flatMap :: (a -> M b) -> M a -> M b
- Pattern Matching
- do construct in Haskell / for construct in Scala
Let’s investigate what the equivalents are in Rust
Working with an Option
Cop
yrig
ht P
rism
Tech
, 201
7
Hardcore functional programmers live out of map and flatMap
Rust’s Option type is equipped with a map defined as:
In Rust’s , flatMap called and_then, is defined as:
The Orthodox Option
fn and_then<U, F>(self, f: F) -> Option<U> where F: FnOnce(T) -> Option<U>
fn map<U, F>(self, f: F) -> Option<U> where F: FnOnce(T) -> U
Cop
yrig
ht P
rism
Tech
, 201
7
As a result the orthodox way of dealing with options is to use map and and_then:
The Orthodox Option
let a = Some(18); let b = Some(24);
let c = a.and_then(|x| { b.map(|y| { x + y }) });
println!("{:?} + {:?} = {:?}", a, b, c);
[rust]: Some(18) + Some(24) = Some(42)
Cop
yrig
ht P
rism
Tech
, 201
7
Another way to work with the Option type is to use pattern matching as shown below:
Pattern Matching Option
let a = Some(18); let b = Some(24);
let c = match a { Some(x) => match b { Some(y) => Some(x+y), None => None }, None => None }; println!("{:?} + {:?} = {:?}", a, b, c);
[rust]: Some(18) + Some(24) = Some(42)
Cop
yrig
ht P
rism
Tech
, 201
7
That was easy. Let’s try with strings…
Ownership and Moves
let s = Some(String::from(“Welcome to")); let t = Some(String::from("Rust!"));
let u = s.and_then(|a| { t.map(|b| { format!("{} {}", a, b).to_string()}) });
println!("{:?} + {:?} = {:?}", s, t, u);
[…]
error[E0382]: use of moved value: `s`
error[E0382]: use of moved value: `t`
Cop
yrig
ht P
rism
Tech
, 201
7
The reason why the apparently innocent example does not compile has to do with Rust Ownership and Move semantics.
Ownership and Moves
let s = Some(String::from(“Welcome to")); let t = Some(String::from("Rust!"));
let u = s.and_then(|a| { t.map(|b| { format!("{} {}", a, b).to_string()}) });
println!("{:?} + {:?} = {:?}", s, t, u);
The moved t is freed hereThe moved s is freed here
Cop
yrig
ht P
rism
Tech
, 201
7
To avoid moving the value held by the option into the lambda we have to use as_ref
Ownership and Moves
let s = Some(String::from(“Welcome to")); let t = Some(String::from("Rust!"));
let u = s.as_ref().and_then(|a| { t.as_ref().map(|b| { format!("{} {}", a, b).to_string()}) });
println!("{:?} + {:?} = {:?}", s, t, u);
[…][rust]: Some(“Welcome to") + Some("Rust!") = Some("Welcome to Rust!")
Cop
yrig
ht P
rism
Tech
, 201
7
You have to watch out for moves also when using pattern matching. As a consequence the following snippet also suffers from “unintended” move
Move & Pattern Matching
let s = Some(String::from(“Welcome to")); let t = Some(String::from(“Rust!"));
let u = match s { Some(a) => match t { Some(b) => Some(format!("{} {}", a, b)), None => None }, None => None };
println!("{:?} + {:?} = {:?}", s, t, u);
The moved s is freed here
The moved t is freed here
Cop
yrig
ht P
rism
Tech
, 201
7
As we’ve seen with and_then/map the trick is to use references. Please notice that as Option implements the Copy trait we need to use the reference only on its content — otherwise we would have had to match by &
Move & Pattern Matching
let s = Some(String::from(“Welcome to")); let t = Some(String::from(“Rust!"));
let u = match s { Some(ref a) => match t { Some(ref b) => Some(format!("{} {}", a, b)), None => None }, None => None };
println!("{:?} + {:?} = {:?}", s, t, u);
Cop
yrig
ht P
rism
Tech
, 201
7
If you are fond of Haskell’s do or Scala’s for construct you can achieve a similar syntactical sugar by using Rust’s iterators
Iterating an Option
let s = Some(String::from(“Welcome to")); let t = Some(String::from(“Rust!")); let mut u = None;
for a in s.iter() { for b in t.iter() { u = Some(format!("{} {}", a, b)) } } println!("{:?} + {:?} = {:?}", s, t, u);
This is not my favourite way as I don’t like to use mutability so casually.
It’d be much nicer if the for-loop would allow to return a value
[…][rust]: Some(“Welcome to") + Some("Rust!") = Some("Welcome to Rust!")