17
Copyright PrismTech, 2017 Angelo Corsaro, PhD CTO, ADLINK Tech. Inc. Co-Chair, OMG DDS-SIG [email protected] RUSTing Partially Ordered Rust Programming Ruminations

RUSTing -- Partially Ordered Rust Programming Ruminations

Embed Size (px)

Citation preview

Page 1: RUSTing -- Partially Ordered Rust Programming Ruminations

Cop

yrig

ht P

rism

Tech

, 201

7

AngeloCorsaro,PhDCTO,ADLINKTech.Inc.Co-Chair,[email protected]

RUSTingPartially Ordered Rust Programming Ruminations

Page 2: RUSTing -- Partially 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

Page 3: RUSTing -- Partially Ordered Rust Programming Ruminations

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?

Page 4: RUSTing -- Partially Ordered Rust Programming Ruminations

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

Page 5: RUSTing -- Partially Ordered Rust Programming Ruminations

Cop

yrig

ht P

rism

Tech

, 201

7

Working with Monads…

Page 6: RUSTing -- Partially Ordered Rust Programming Ruminations

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

Page 7: RUSTing -- Partially Ordered Rust Programming Ruminations

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), }

Page 8: RUSTing -- Partially Ordered Rust Programming Ruminations

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

Page 9: RUSTing -- Partially Ordered Rust Programming Ruminations

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

Page 10: RUSTing -- Partially Ordered Rust Programming Ruminations

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)

Page 11: RUSTing -- Partially Ordered Rust Programming Ruminations

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)

Page 12: RUSTing -- Partially Ordered Rust Programming Ruminations

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`

Page 13: RUSTing -- Partially Ordered Rust Programming Ruminations

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

Page 14: RUSTing -- Partially Ordered Rust Programming Ruminations

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!")

Page 15: RUSTing -- Partially Ordered Rust Programming Ruminations

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

Page 16: RUSTing -- Partially Ordered Rust Programming Ruminations

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);

Page 17: RUSTing -- Partially Ordered Rust Programming Ruminations

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!")