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.
2. CopyrightPrismTech,2017
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
3. CopyrightPrismTech,2017
Rust is a system programming
language that provides zero
cost high-level abstractions
and language-enforced
memory and concurrency
safety.
What is Rust?
4. CopyrightPrismTech,2017
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
6. CopyrightPrismTech,2017
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
7. CopyrightPrismTech,2017
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),
}
8. CopyrightPrismTech,2017
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
9. CopyrightPrismTech,2017
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
10. CopyrightPrismTech,2017
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)
11. CopyrightPrismTech,2017
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)
12. CopyrightPrismTech,2017
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`
13. CopyrightPrismTech,2017
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 here
The moved s is freed here
14. CopyrightPrismTech,2017
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!")
15. CopyrightPrismTech,2017
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
16. CopyrightPrismTech,2017
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);
17. CopyrightPrismTech,2017
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!")