Slide 1

Slide 1 text

UST ME, I’m a developer! gregmalcolm speakerdeck: gregmalcolm/rust-me-im-a-developer “Rust me, I’m a developer”. A beginners overview of Rust.

Slide 2

Slide 2 text

True Facts ! About Me gregmalcolm speakerdeck: gregmalcolm/rust-me-im-a-developer Blah blah blah, my name is Greg Malcolm. Currently I’m working with Ruby on Rails and Javascript. The job before that I worked with embedded C++. The job before that I worked with .NET and Java. And so on. See the pattern? I enjoy trying out new languages. And my quest for the new and shiny has lead me to…

Slide 3

Slide 3 text

True Facts ! About Me Not from Wisconsin… gregmalcolm speakerdeck: gregmalcolm/rust-me-im-a-developer Do they drink tea in Peru? Nah, Australian right? Blah blah blah, my name is Greg Malcolm. Currently I’m working with Ruby on Rails and Javascript. The job before that I worked with embedded C++. The job before that I worked with .NET and Java. And so on. See the pattern? I enjoy trying out new languages. And my quest for the new and shiny has lead me to…

Slide 4

Slide 4 text

Rust is currently on version 0.10. Until we get to version 1.0 the code is subject to radical change. That said things are starting to calm down a lot, and version 1.0 should be coming out in the next year.

Slide 5

Slide 5 text

An alternative to C and C++ RUST is a “Systems Programming Language”. It’s for writing software that serves the platform rather than the application layer; So it’s competing with the likes of C and C++. ! Let’s talk about C and C++ for a moment. The upside of working with C and C++ is it’s really fast and efficient and really takes you close to the metal. It allows you to address the computers memory systems directly and will compile natively on any operating system. It’s as good as it gets without resorting to assembly language programming which can be difficult to maintain. It also stays clear of using garbage collection, which drains resources and causes periodic slowdowns. However, there are downsides, one of those being that it’s really easy to accidentally introduce bugs that blow up intermittently and when you least expect it. Tracking down these problems can be very tricky. More on that in a bit.

Slide 6

Slide 6 text

use std::io::timer::sleep; ! // Rust 0.10 fn main() { let actions = [("Captain America", "bashes", 20), ("Black Widow", "slashes", 25), ("Ironman", "throws cash at", 0), ("Hulk", "SMASHES", 200)]; ! let mut outcomes = actions.iter() .map(|&action| { let (hero, attack, damage) = action; format!("{:s} {:s} Red Skull for {:d} damage", hero, attack, damage) }); ! for outcome in outcomes { spawn(proc() { sleep(500); println!(“{:s}”, outcome); }); } } Rust sample Lets take a look at a little sample as a taster… ! In this scenario the Avengers have just rolled for initiative in a battle against Red Skull we want to show the results. ! The actions are declared in the form of a vector of tuples. Rust calls arrays “vectors”, much like C++. Each tuple is made up of a hero string, an attack string and an integer value giving damage. ! Next we need to figure out the outcome of each action to output. We iterate though each action tuple transforming the tuple into a string using the map() method. Ruby developers will notice the inside of map() looks similar to a Ruby block. This is called a closure in Rust. Inside the closure we’ll decode the tuple contents to hero, attack and damage variables. format!() will create a sanitized string output replacing the {:s}, {:s} and {:d} with hero, attack and damage. ! Finally in the for loop at the end we’ll iterate through each outcome string and display it. We’re wrapping the println!() statement in a spawn() block to make each output happen asynchronously in it’s own lightweight thread. Rust is really good at concurrency and multithreading, we’ll talk about that later. The sleep() statement helps induce the 4 outcomes to display in random order.

Slide 7

Slide 7 text

Rust sample $ rustc intro.rs ! $ ./intro ! Captain America bashes Red Skull for 20 damage! Ironman throws cash at Red Skull for 0 damage! Black Widow slashes Red Skull for 25 damage! Hulk SMASHES Red Skull for 200 damage! $ ./intro! Black Widow slashes Red Skull for 25 damage! Captain America bashes Red Skull for 20 damage! Ironman throws cash at Red Skull for 0 damage! Hulk SMASHES Red Skull for 200 damage! $ I compile the sample using the compiler, rustc. This will give me a native binary that I can execute.

Slide 8

Slide 8 text

Consider browsers… Let’s talk about browsers. Browsers need to be extremely fast. Therefore all the major browsers make use of a lot of C/C++ code. For example I think read somewhere that Mozilla uses something like 7 million lines of C and C++. When you’ve got that much C++ code safety is a problem. So much so that Mozilla have started funding Rust as a full time research project as an alternative language providing speed, efficiency AND safety. They are currently using it to build a new experimental browser, called…

Slide 9

Slide 9 text

… Servo. Named after Tom Servo, one of the robots in MST3K.

Slide 10

Slide 10 text

int main() { char *str = "BOOM"; str[0] = 'Z'; ! return 0; } C Boom! Let’s take a look at life in C. Anyone guess what this code does? Well, it look’s like it’s trying to change the word BOOM into ZOOM. Only problem is that we’ve initialized it to the string “ZOOM” which actually makes it a constant. Does the compiler tell you? No.

Slide 11

Slide 11 text

int main() { char *str = "BOOM"; str[0] = 'Z'; ! return 0; } C $ clang boom.c -o boom $ Boom! Let’s take a look at life in C. Anyone guess what this code does? Well, it look’s like it’s trying to change the word BOOM into ZOOM. Only problem is that we’ve initialized it to the string “ZOOM” which actually makes it a constant. Does the compiler tell you? No.

Slide 12

Slide 12 text

int main() { char *str = "BOOM"; str[0] = 'Z'; ! return 0; } C $ clang boom.c -o boom ! $ ./boom ! [1] 38116 bus error ./boom! $ Boom! However when we do see a problem at runtime. It turns out that when a string is defined this way, it’s actually a constant string. Therefore changing out the first character causes explosions.

Slide 13

Slide 13 text

int main() { char *str = "BOOM"; str[0] = 'Z'; ! return 0; } C $ clang boom.c -o boom ! $ ./boom ! [1] 38116 bus error ./boom! $ Boom! However when we do see a problem at runtime. It turns out that when a string is defined this way, it’s actually a constant string. Therefore changing out the first character causes explosions.

Slide 14

Slide 14 text

Boom! fn main() { let s = ~"BOOM"; s.shift_char(); s.unshift_char('Z'); ! println!("It go {:s}", s); } Rust Here is a similar version of the code written in Rust. Putting a “~” in front of the string makes it an “owned pointer” to a string on the heap which we need to do if we want a changeable string. Don’t worry about that too much for now. We use shift_char() to remove the ‘B’, and unshift_char() to prepend a ‘Z’ in it’s place. ! Rust warns you about problems at compile time rather than runtime. Compiling this program exposes a problem: “cannot borrow immutable local variable as mutable”. Turns out that Rust actually defines immutable constants by default. This is advantageous for optimization and concurrency concerns.

Slide 15

Slide 15 text

Boom! fn main() { let s = ~"BOOM"; s.shift_char(); s.unshift_char('Z'); ! println!("It go {:s}", s); } $ rustc less_boom.rs less_boom.rs:3:5: 3:6 error: cannot borrow immutable local variable as mutable less_boom.rs:3 s.shift_char(); ^ less_boom.rs:4:5: 4:6 error: cannot borrow immutable local variable as mutable less_boom.rs:4 s.unshift_char('Z'); ^ Rust Here is a similar version of the code written in Rust. Putting a “~” in front of the string makes it an “owned pointer” to a string on the heap which we need to do if we want a changeable string. Don’t worry about that too much for now. We use shift_char() to remove the ‘B’, and unshift_char() to prepend a ‘Z’ in it’s place. ! Rust warns you about problems at compile time rather than runtime. Compiling this program exposes a problem: “cannot borrow immutable local variable as mutable”. Turns out that Rust actually defines immutable constants by default. This is advantageous for optimization and concurrency concerns.

Slide 16

Slide 16 text

Boom! fn main() { let mut s = ~"BOOM"; s.shift_char(); s.unshift_char('Z'); ! println!("It go {:s}", s); } Rust We just need to add “mut” to the declaration to make the variable mutable. Fixed!

Slide 17

Slide 17 text

Boom! fn main() { let mut s = ~"BOOM"; s.shift_char(); s.unshift_char('Z'); ! println!("It go {:s}", s); } $ rustc less_boom.rs $ Rust We just need to add “mut” to the declaration to make the variable mutable. Fixed!

Slide 18

Slide 18 text

Boom! fn main() { let mut s = ~"BOOM"; s.shift_char(); s.unshift_char('Z'); ! println!("It go {:s}", s); } $ rustc less_boom.rs! $ ./less_boom! It go ZOOM! $ Rust And it executes too.

Slide 19

Slide 19 text

#include #include int main() { int *dangler = NULL; if (true) { int temp=23; dangler = &temp; } printf("Dangler equals: %d \n", *dangler); ! return 1; } Dangling Pointers C That last example was a little unfair. C++ would have handled it more gracefully. Dangling pointers however are a very serious issue in C and C++. Let’s take a look at how this sample works…

Slide 20

Slide 20 text

#include #include int main() { int *dangler = NULL; if (true) { int temp=23; dangler = &temp; } printf("Dangler equals: %d \n", *dangler); ! return 1; } Dangling Pointers C ‘dangler’ is defined as pointer to an integer. It is located at memory address 0x1E00. ! Next ‘dangler’ is set to point to the variable ‘temp’ at memory location 0x1E01. If dereferenced it will return the value 23. ! However, dangler is dereferenced on the printf statement after the temp variable goes out of scope. What will happen when we reach this line?

Slide 21

Slide 21 text

#include #include int main() { int *dangler = NULL; if (true) { int temp=23; dangler = &temp; } printf("Dangler equals: %d \n", *dangler); ! return 1; } Dangling Pointers C The Stack 0x1E00 dangler ! ! =NULL ‘dangler’ is defined as pointer to an integer. It is located at memory address 0x1E00. ! Next ‘dangler’ is set to point to the variable ‘temp’ at memory location 0x1E01. If dereferenced it will return the value 23. ! However, dangler is dereferenced on the printf statement after the temp variable goes out of scope. What will happen when we reach this line?

Slide 22

Slide 22 text

#include #include int main() { int *dangler = NULL; if (true) { int temp=23; dangler = &temp; } printf("Dangler equals: %d \n", *dangler); ! return 1; } Dangling Pointers C The Stack 0x1E00 dangler 0x1E01 temp =23 =&0x1E01 (23) ‘dangler’ is defined as pointer to an integer. It is located at memory address 0x1E00. ! Next ‘dangler’ is set to point to the variable ‘temp’ at memory location 0x1E01. If dereferenced it will return the value 23. ! However, dangler is dereferenced on the printf statement after the temp variable goes out of scope. What will happen when we reach this line?

Slide 23

Slide 23 text

#include #include int main() { int *dangler = NULL; if (true) { int temp=23; dangler = &temp; } printf("Dangler equals: %d \n", *dangler); ! return 1; } Dangling Pointers C The Stack 0x1E00 dangler=&0x1E01 (?) ! 0x1E01 temp =23 ‘dangler’ is defined as pointer to an integer. It is located at memory address 0x1E00. ! Next ‘dangler’ is set to point to the variable ‘temp’ at memory location 0x1E01. If dereferenced it will return the value 23. ! However, dangler is dereferenced on the printf statement after the temp variable goes out of scope. What will happen when we reach this line?

Slide 24

Slide 24 text

#include #include int main() { int *dangler = NULL; if (true) { int temp=23; dangler = &temp; } printf("Dangler equals: %d \n", *dangler); ! return 1; } $ clang dangle.c -o dangle $ Dangling Pointers C ‘dangler’ is defined as pointer to an integer. It is located at memory address 0x1E00. ! Next ‘dangler’ is set to point to the variable ‘temp’ at memory location 0x1E01. If dereferenced it will return the value 23. ! However, dangler is dereferenced on the printf statement after the temp variable goes out of scope. What will happen when we reach this line?

Slide 25

Slide 25 text

#include #include int main() { int *dangler = NULL; if (true) { int temp=23; dangler = &temp; } printf("Dangler equals: %d \n", *dangler); ! return 1; } Dangling Pointers C The answer is “undetermined”. This time around we actually get back 23 because the memory hasn’t been reallocated yet. But what if the memory had been reallocated? Maybe 9 times out of 10 this line will return the expected result in production. And on the other occasion crash the program. What if you changed the value referenced at this location? You could be changing memory in some other part of the application. Could get crazy! These kinds of problems can spot during testing, let alone troubleshooting and fixing.

Slide 26

Slide 26 text

#include #include int main() { int *dangler = NULL; if (true) { int temp=23; dangler = &temp; } printf("Dangler equals: %d \n", *dangler); ! return 1; } $ clang dangle.c -o dangle! $ ./dangle ! Dangler equals: 23 ! $ Dangling Pointers C The answer is “undetermined”. This time around we actually get back 23 because the memory hasn’t been reallocated yet. But what if the memory had been reallocated? Maybe 9 times out of 10 this line will return the expected result in production. And on the other occasion crash the program. What if you changed the value referenced at this location? You could be changing memory in some other part of the application. Could get crazy! These kinds of problems can spot during testing, let alone troubleshooting and fixing.

Slide 27

Slide 27 text

fn main() { let dangler; if true { let temp = 23; dangler = &temp; } println!("Dangler equals: {:d}", *dangler); } Rust Dangling Pointers Not a problem in Rust. It’ll warn you at compile time that you’re doing something hazardous. Let’s try compiling this.

Slide 28

Slide 28 text

fn main() { let dangler; if true { let temp = 23; dangler = &temp; } println!("Dangler equals: {:d}", *dangler); } $ rustc less_dangle.rs less_dangle.rs:5:19: 5:24 error: `temp` does not live long enough less_dangle.rs:5 dangler = &temp; ^~~~~ less_dangle.rs:1:11: 8:2 note: reference must be valid for the block at 1:10... less_dangle.rs:1 fn main() { less_dangle.rs:2 let dangler; less_dangle.rs:3 if (true) { less_dangle.rs:4 let temp = 23; less_dangle.rs:5 dangler = &temp; less_dangle.rs:6 } ... less_dangle.rs:3:15: 6:6 note: ...but borrowed value is only valid for the block at less_dangle.rs:3 if (true) { Rust Dangling Pointers Not a problem in Rust. It’ll warn you at compile time that you’re doing something hazardous. Let’s try compiling this.

Slide 29

Slide 29 text

fn main() { let dangler; if true { let temp = 23; dangler = &temp; } println!("Dangler equals: {:d}", *dangler); } less_dangle.rs:7:39: 7:46 error: use of possibly uninitialized variable: `dangler`! less_dangle.rs:7 println!("Dangler equals: {:d}", *dangler);! ^~~~~~~ Rust Dangling Pointers First problem: it doesn’t like that we’re potentially initializing dangler in an inconsistent way.

Slide 30

Slide 30 text

fn main() { let dangler; if true { let temp = 23; dangler = &temp; } else { // dangler isn’t defined for this case! } println!("Dangler equals: {:d}", *dangler); } Dangling Pointers Rust less_dangle.rs:7:39: 7:46 error: use of possibly uninitialized variable: `dangler`! less_dangle.rs:7 println!("Dangler equals: {:d}", *dangler);! ^~~~~~~ Ok, this one is one me. By declaring dangler on the outside of the if statement and then defining it inside I’ve made the definition ambiguous. What if there was an else cause? It could be defined as something else entirely.

Slide 31

Slide 31 text

fn main() { let dangler; { let temp = 23; dangler = &temp; } println!("Dangler equals: {:d}", *dangler); } less_dangle.rs:5:19: 5:24 error: `temp` does not live long enough! less_dangle.rs:5 dangler = &temp;! ^~~~~! less_dangle.rs:1:11: 8:2 note: reference must be valid for the block at 1:10! ...! less_dangle.rs:3:15: 6:6 note: ...but borrowed value is only valid for the block at 3:14! less_dangle.rs:3 if (true) {! less_dangle.rs:4 let temp = 23;! less_dangle.rs:5 dangler = &temp;! less_dangle.rs:6 } Dangling Pointers Rust No problem. We don’t really need an if statement, let’s just use empty braces instead. ! Next up: “temp does not live long enough”. Yup! We used dangler after it “died”. ! But what’s this? “Borrowed value does not live long enough”? What does that mean?

Slide 32

Slide 32 text

fn main() { let dangler; { let temp = 23; dangler = &temp; } println!("Dangler equals: {:d}", *dangler); } less_dangle.rs:5:19: 5:24 error: `temp` does not live long enough! less_dangle.rs:5 dangler = &temp;! ^~~~~! less_dangle.rs:1:11: 8:2 note: reference must be valid for the block at 1:10! ...! less_dangle.rs:3:15: 6:6 note: ...but borrowed value is only valid for the block at 3:14! less_dangle.rs:3 if (true) {! less_dangle.rs:4 let temp = 23;! less_dangle.rs:5 dangler = &temp;! less_dangle.rs:6 } Dangling Pointers Rust No problem. We don’t really need an if statement, let’s just use empty braces instead. ! Next up: “temp does not live long enough”. Yup! We used dangler after it “died”. ! But what’s this? “Borrowed value does not live long enough”? What does that mean?

Slide 33

Slide 33 text

Borrowing (&) let shield = Shield::new(); 1) For this scenario Captain America has created a shield so he can fight crime. 2) But Stephen Colbert wants to fight crime too! 3) Stephen Colbert borrows the shield by dereferencing the shield variable. Stephen now has the shield so Captain America can’t use it while he has it. 4) But because this is called “borrowing”, Stephen eventually has to return the shield. Now Captain America can now go back to fighting crime,and Stephen Colbert can go back to feeling sad.

Slide 34

Slide 34 text

Borrowing (&) let shield = Shield::new(); 1) For this scenario Captain America has created a shield so he can fight crime. 2) But Stephen Colbert wants to fight crime too! 3) Stephen Colbert borrows the shield by dereferencing the shield variable. Stephen now has the shield so Captain America can’t use it while he has it. 4) But because this is called “borrowing”, Stephen eventually has to return the shield. Now Captain America can now go back to fighting crime,and Stephen Colbert can go back to feeling sad.

Slide 35

Slide 35 text

Borrowing (&) let colbert = &shield; 1) For this scenario Captain America has created a shield so he can fight crime. 2) But Stephen Colbert wants to fight crime too! 3) Stephen Colbert borrows the shield by dereferencing the shield variable. Stephen now has the shield so Captain America can’t use it while he has it. 4) But because this is called “borrowing”, Stephen eventually has to return the shield. Now Captain America can now go back to fighting crime,and Stephen Colbert can go back to feeling sad.

Slide 36

Slide 36 text

Borrowing (&) 1) For this scenario Captain America has created a shield so he can fight crime. 2) But Stephen Colbert wants to fight crime too! 3) Stephen Colbert borrows the shield by dereferencing the shield variable. Stephen now has the shield so Captain America can’t use it while he has it. 4) But because this is called “borrowing”, Stephen eventually has to return the shield. Now Captain America can now go back to fighting crime,and Stephen Colbert can go back to feeling sad.

Slide 37

Slide 37 text

fn main() { let dangler; { let temp = 23; dangler = &temp; } println!("Dangler equals: {:d}", *dangler); } Rust Dangling Pointers less_dangle.rs:5:19: 5:24 error: `temp` does not live long enough! less_dangle.rs:5 dangler = &temp;! ^~~~~! less_dangle.rs:1:11: 8:2 note: reference must be valid for the block at 1:10! ...! less_dangle.rs:3:15: 6:6 note: ...but borrowed value is only valid for the block at 3:14! less_dangle.rs:3 if (true) {! less_dangle.rs:4 let temp = 23;! less_dangle.rs:5 dangler = &temp;! less_dangle.rs:6 } So dangler was borrowing the reference to temp and didn’t return it before temp expired. That’s not so much borrowing as stealing! And Rust will have no truck with that kind of behavior. This is one of the mechanisms Rust uses to keep track of all memory allocations and thus prevent Bad Things happening.

Slide 38

Slide 38 text

So lets talk about pointers some more. Shared pointers!

Slide 39

Slide 39 text

I’ll explain in the form of a code sample emulating a famous scene from the film Spartacus where Spartacus is trying to surrender to the romans, by declaring “I’m Spartacus”. One of his men then yells “No, I’m Spartacus”. And then they all start yelling it. It’s a fun thing to shout!

Slide 40

Slide 40 text

I’ll explain in the form of a code sample emulating a famous scene from the film Spartacus where Spartacus is trying to surrender to the romans, by declaring “I’m Spartacus”. One of his men then yells “No, I’m Spartacus”. And then they all start yelling it. It’s a fun thing to shout!

Slide 41

Slide 41 text

fn main() { let spartacus = ~"Spartacus"; println!("I'm {:?}!", spartacus); } Owned Pointers (~) So, here’s that squiggle again. Ok, a tilde if you insist! Rather than declaring Spartacus as a stack variable I’d like to allocate it on the Heap instead. That way it’s not going to disappear just because it’s lost scope. Also if I’d created it statically, it would just get stored as a series of unchangeable byte characters. ! On this occasion I’m formatting the output using the {:?} type. This displays the content as “debugger information” rather than casting it as something specific. It’s ugly and computationally expensive though, so no putting it in production code!

Slide 42

Slide 42 text

fn main() { let spartacus = ~"Spartacus"; println!("I'm {:?}!", spartacus); } Owned Pointers (~) So, here’s that squiggle again. Ok, a tilde if you insist! Rather than declaring Spartacus as a stack variable I’d like to allocate it on the Heap instead. That way it’s not going to disappear just because it’s lost scope. Also if I’d created it statically, it would just get stored as a series of unchangeable byte characters. ! On this occasion I’m formatting the output using the {:?} type. This displays the content as “debugger information” rather than casting it as something specific. It’s ugly and computationally expensive though, so no putting it in production code!

Slide 43

Slide 43 text

fn main() { let spartacus = ~"Spartacus"; println!("I'm {:?}!", spartacus); } I'm ~"Spartacus"! Owned Pointers (~) So, here’s that squiggle again. Ok, a tilde if you insist! Rather than declaring Spartacus as a stack variable I’d like to allocate it on the Heap instead. That way it’s not going to disappear just because it’s lost scope. Also if I’d created it statically, it would just get stored as a series of unchangeable byte characters. ! On this occasion I’m formatting the output using the {:?} type. This displays the content as “debugger information” rather than casting it as something specific. It’s ugly and computationally expensive though, so no putting it in production code!

Slide 44

Slide 44 text

fn main() { let spartacus = ~"Spartacus"; println!("I'm {:?}!", spartacus); ! let wonder_woman = spartacus; println!("No, I'm {:?}!!", wonder_woman); } Owned Pointers (~) Next up Wonder Woman steps in to take the rap. She takes ownership of the owned pointer by assigning herself to the spartacus variable. Only one variable is actually allowed to own an owned pointer, so ownership Moves to Wonder Woman moving it away from Spartacus. So now Wonder Woman gets to be Spartacus!

Slide 45

Slide 45 text

fn main() { let spartacus = ~"Spartacus"; println!("I'm {:?}!", spartacus); ! let wonder_woman = spartacus; println!("No, I'm {:?}!!", wonder_woman); } Owned Pointers (~) Next up Wonder Woman steps in to take the rap. She takes ownership of the owned pointer by assigning herself to the spartacus variable. Only one variable is actually allowed to own an owned pointer, so ownership Moves to Wonder Woman moving it away from Spartacus. So now Wonder Woman gets to be Spartacus!

Slide 46

Slide 46 text

fn main() { let spartacus = ~"Spartacus"; println!("I'm {:?}!", spartacus); ! let wonder_woman = spartacus; println!("No, I'm {:?}!!", wonder_woman); } I'm ~"Spartacus"! No, I'm ~"Spartacus"!! Owned Pointers (~) Next up Wonder Woman steps in to take the rap. She takes ownership of the owned pointer by assigning herself to the spartacus variable. Only one variable is actually allowed to own an owned pointer, so ownership Moves to Wonder Woman moving it away from Spartacus. So now Wonder Woman gets to be Spartacus!

Slide 47

Slide 47 text

fn main() { let spartacus = ~"Spartacus"; println!("I'm {:?}!", spartacus); ! let wonder_woman = spartacus; println!("No, I'm {:?}!!", wonder_woman); ! let hulk = wonder_woman; println!("NO! HULK IS {:?}!!!", hulk); } Owned Pointers (~) Similarly Hulk in turn can also take over ownership of the owned pointer by taking it from Wonder Woman. ! ‘NO! HULK IS ~”Spartacus”!!!’ ! Hulk really doesn’t get how this works…

Slide 48

Slide 48 text

fn main() { let spartacus = ~"Spartacus"; println!("I'm {:?}!", spartacus); ! let wonder_woman = spartacus; println!("No, I'm {:?}!!", wonder_woman); ! let hulk = wonder_woman; println!("NO! HULK IS {:?}!!!", hulk); } Owned Pointers (~) Similarly Hulk in turn can also take over ownership of the owned pointer by taking it from Wonder Woman. ! ‘NO! HULK IS ~”Spartacus”!!!’ ! Hulk really doesn’t get how this works…

Slide 49

Slide 49 text

fn main() { let spartacus = ~"Spartacus"; println!("I'm {:?}!", spartacus); ! let wonder_woman = spartacus; println!("No, I'm {:?}!!", wonder_woman); ! let hulk = wonder_woman; println!("NO! HULK IS {:?}!!!", hulk); } I'm ~"Spartacus"! No, I'm ~"Spartacus"!! NO! HULK IS ~"Spartacus"!!! Owned Pointers (~) Similarly Hulk in turn can also take over ownership of the owned pointer by taking it from Wonder Woman. ! ‘NO! HULK IS ~”Spartacus”!!!’ ! Hulk really doesn’t get how this works…

Slide 50

Slide 50 text

fn main() { let spartacus = ~"Spartacus"; println!("I'm {:s}!", spartacus); ! let wonder_woman = spartacus; println!("No, I'm {:s}!!", wonder_woman); ! let hulk = wonder_woman; println!("NO! HULK IS {:s}!!!", hulk); } I'm Spartacus!! No, I'm Spartacus!!! NO! HULK IS Spartacus!!! Owned Pointers (~) I think we’ve made it pretty clear that we’re outputting owned strings, so I’m going to switch out the output to plain strings. Much cleaner!

Slide 51

Slide 51 text

fn main() { let spartacus = ~"Spartacus"; println!("I'm {:s}!", spartacus); ! let wonder_woman = spartacus; println!("No, I'm {:s}!!", wonder_woman); ! let hulk = wonder_woman; println!("NO! HULK IS {:s}!!!", hulk); ! println!("No really, I am {:s} :(", spartacus); } Owned Pointers (~) Spartacus at this point tries to set everyone straight. But because ownership of ~”Spartacus” has moved, accessing the spartacus variable is doomed to compiler failure.

Slide 52

Slide 52 text

fn main() { let spartacus = ~"Spartacus"; println!("I'm {:s}!", spartacus); ! let wonder_woman = spartacus; println!("No, I'm {:s}!!", wonder_woman); ! let hulk = wonder_woman; println!("NO! HULK IS {:s}!!!", hulk); ! println!("No really, I am {:s} :(", spartacus); } spartacus.rs:11:39: 11:48 error: use of moved value: `spartacus` spartacus.rs:11 println!("No really, I am {:s}!", spartacus); ^~~~~~~~~ Owned Pointers (~) Spartacus at this point tries to set everyone straight. But because ownership of ~”Spartacus” has moved, accessing the spartacus variable is doomed to compiler failure.

Slide 53

Slide 53 text

fn main() { let spartacus = "Spartacus"; println!("I'm {:s}!", spartacus); ! let wonder_woman = spartacus; println!("No, I'm {:s}!!", wonder_woman); ! let hulk = wonder_woman; println!("NO! HULK IS {:s}!!!", hulk); ! println!("No really, I am {:s} :(", spartacus); } Owned Pointers (~) Of course we could just declare a normal string instead of an “owned” string. The we would be passing around a reference pointer which would not get moved. We would be subject to Borrowing restrictions rather than Moving restrictions. Of course they’re all wrong…

Slide 54

Slide 54 text

fn main() { let spartacus = "Spartacus"; println!("I'm {:s}!", spartacus); ! let wonder_woman = spartacus; println!("No, I'm {:s}!!", wonder_woman); ! let hulk = wonder_woman; println!("NO! HULK IS {:s}!!!", hulk); ! println!("No really, I am {:s} :(", spartacus); } I'm Spartacus! No, I'm Spartacus!! NO! HULK IS Spartacus!!! No really, I am Spartacus :( Owned Pointers (~) Of course we could just declare a normal string instead of an “owned” string. The we would be passing around a reference pointer which would not get moved. We would be subject to Borrowing restrictions rather than Moving restrictions. Of course they’re all wrong…

Slide 55

Slide 55 text

Of course they’re all wrong. Morpheus is Spartacus

Slide 56

Slide 56 text

Of course they’re all wrong. Morpheus is Spartacus

Slide 57

Slide 57 text

struct Liberator { name: ~str, favorite_color: &'static str } ! fn defiance(prefix: &str, liberator: &mut Liberator) { liberator.name.push_char('!'); println!("{:s} {:s}", prefix, liberator.name); } ! fn main() { let mut spartacus = ~Liberator { name: ~"Spartacus", favorite_color: "chartreuse" }; defiance("I'm", spartacus); ! let mut wonder_woman = spartacus; defiance("No I'm", wonder_woman); ! let mut hulk = wonder_woman; defiance("NO! HULK IS", hulk); } Let’s throw something new into the mix: structs. I declare a liberator struct at the top here. I’ve decided the name is like before, an owned string, but for the favorite color I just decided to keep it static. Because I’m effectively borrowing a static value in a struct like this the compiler needs some kind of clue about how long lived it is, so I add the special lifetime type, ‘static, which lasts the length of the application. ! When I assign spartacus to an instance of Liberator I have to declare all the struct's properties (name and favorite_color). Yes, Spartacus’ favorite color is chartreuse. Not many people know that. ! I’ve also brought in a function, defiance() to handle all that glorious shouting. In the definition of defiance() we’re receiving the arguments as references. These parameters finish borrowing when the function has completed, so borrowing works pretty well here. I’m defined the liberator parameter as mutable so we can add extra exclamation marks to the name. ! End results: same results as before. This is just a refactor.

Slide 58

Slide 58 text

struct Liberator { name: ~str, favorite_color: &'static str } ! fn defiance(prefix: &str, liberator: &mut Liberator) { liberator.name.push_char('!'); println!("{:s} {:s}", prefix, liberator.name); } ! fn main() { let mut spartacus = ~Liberator { name: ~"Spartacus", favorite_color: "chartreuse" }; defiance("I'm", spartacus); ! let mut wonder_woman = spartacus; defiance("No I'm", wonder_woman); ! let mut hulk = wonder_woman; defiance("NO! HULK IS", hulk); } Let’s throw something new into the mix: structs. I declare a liberator struct at the top here. I’ve decided the name is like before, an owned string, but for the favorite color I just decided to keep it static. Because I’m effectively borrowing a static value in a struct like this the compiler needs some kind of clue about how long lived it is, so I add the special lifetime type, ‘static, which lasts the length of the application. ! When I assign spartacus to an instance of Liberator I have to declare all the struct's properties (name and favorite_color). Yes, Spartacus’ favorite color is chartreuse. Not many people know that. ! I’ve also brought in a function, defiance() to handle all that glorious shouting. In the definition of defiance() we’re receiving the arguments as references. These parameters finish borrowing when the function has completed, so borrowing works pretty well here. I’m defined the liberator parameter as mutable so we can add extra exclamation marks to the name. ! End results: same results as before. This is just a refactor.

Slide 59

Slide 59 text

struct Liberator { name: ~str, favorite_color: &'static str } ! fn defiance(prefix: &str, liberator: &mut Liberator) { liberator.name.push_char('!'); println!("{:s} {:s}", prefix, liberator.name); } ! fn main() { let mut spartacus = ~Liberator { name: ~"Spartacus", favorite_color: "chartreuse" }; defiance("I'm", spartacus); ! let mut wonder_woman = spartacus; defiance("No I'm", wonder_woman); ! let mut hulk = wonder_woman; defiance("NO! HULK IS", hulk); } Let’s throw something new into the mix: structs. I declare a liberator struct at the top here. I’ve decided the name is like before, an owned string, but for the favorite color I just decided to keep it static. Because I’m effectively borrowing a static value in a struct like this the compiler needs some kind of clue about how long lived it is, so I add the special lifetime type, ‘static, which lasts the length of the application. ! When I assign spartacus to an instance of Liberator I have to declare all the struct's properties (name and favorite_color). Yes, Spartacus’ favorite color is chartreuse. Not many people know that. ! I’ve also brought in a function, defiance() to handle all that glorious shouting. In the definition of defiance() we’re receiving the arguments as references. These parameters finish borrowing when the function has completed, so borrowing works pretty well here. I’m defined the liberator parameter as mutable so we can add extra exclamation marks to the name. ! End results: same results as before. This is just a refactor.

Slide 60

Slide 60 text

struct Liberator { name: ~str, favorite_color: &'static str } ! fn defiance(prefix: &str, liberator: &mut Liberator) { liberator.name.push_char('!'); println!("{:s} {:s}", prefix, liberator.name); } ! fn main() { let mut spartacus = ~Liberator { name: ~"Spartacus", favorite_color: "chartreuse" }; defiance("I'm", spartacus); ! let mut wonder_woman = spartacus; defiance("No I'm", wonder_woman); ! let mut hulk = wonder_woman; defiance("NO! HULK IS", hulk); } Let’s throw something new into the mix: structs. I declare a liberator struct at the top here. I’ve decided the name is like before, an owned string, but for the favorite color I just decided to keep it static. Because I’m effectively borrowing a static value in a struct like this the compiler needs some kind of clue about how long lived it is, so I add the special lifetime type, ‘static, which lasts the length of the application. ! When I assign spartacus to an instance of Liberator I have to declare all the struct's properties (name and favorite_color). Yes, Spartacus’ favorite color is chartreuse. Not many people know that. ! I’ve also brought in a function, defiance() to handle all that glorious shouting. In the definition of defiance() we’re receiving the arguments as references. These parameters finish borrowing when the function has completed, so borrowing works pretty well here. I’m defined the liberator parameter as mutable so we can add extra exclamation marks to the name. ! End results: same results as before. This is just a refactor.

Slide 61

Slide 61 text

struct Liberator { name: ~str, favorite_color: &'static str } ! fn defiance(prefix: &str, liberator: &mut Liberator) { liberator.name.push_char('!'); println!("{:s} {:s}", prefix, liberator.name); } ! fn main() { let mut spartacus = ~Liberator { name: ~"Spartacus", favorite_color: "chartreuse" }; defiance("I'm", spartacus); ! let mut wonder_woman = spartacus; defiance("No I'm", wonder_woman); ! let mut hulk = wonder_woman; defiance("NO! HULK IS", hulk); } Let’s throw something new into the mix: structs. I declare a liberator struct at the top here. I’ve decided the name is like before, an owned string, but for the favorite color I just decided to keep it static. Because I’m effectively borrowing a static value in a struct like this the compiler needs some kind of clue about how long lived it is, so I add the special lifetime type, ‘static, which lasts the length of the application. ! When I assign spartacus to an instance of Liberator I have to declare all the struct's properties (name and favorite_color). Yes, Spartacus’ favorite color is chartreuse. Not many people know that. ! I’ve also brought in a function, defiance() to handle all that glorious shouting. In the definition of defiance() we’re receiving the arguments as references. These parameters finish borrowing when the function has completed, so borrowing works pretty well here. I’m defined the liberator parameter as mutable so we can add extra exclamation marks to the name. ! End results: same results as before. This is just a refactor.

Slide 62

Slide 62 text

struct Liberator { name: ~str, favorite_color: &'static str } ! fn defiance(prefix: &str, liberator: &mut Liberator) { liberator.name.push_char('!'); println!("{:s} {:s}", prefix, liberator.name); } ! fn main() { let mut spartacus = ~Liberator { name: ~"Spartacus", favorite_color: "chartreuse" }; defiance("I'm", spartacus); ! let mut wonder_woman = spartacus; defiance("No I'm", wonder_woman); ! let mut hulk = wonder_woman; defiance("NO! HULK IS", hulk); } I'm Spartacus! No I'm Spartacus!! NO! HULK IS Spartacus!!! Let’s throw something new into the mix: structs. I declare a liberator struct at the top here. I’ve decided the name is like before, an owned string, but for the favorite color I just decided to keep it static. Because I’m effectively borrowing a static value in a struct like this the compiler needs some kind of clue about how long lived it is, so I add the special lifetime type, ‘static, which lasts the length of the application. ! When I assign spartacus to an instance of Liberator I have to declare all the struct's properties (name and favorite_color). Yes, Spartacus’ favorite color is chartreuse. Not many people know that. ! I’ve also brought in a function, defiance() to handle all that glorious shouting. In the definition of defiance() we’re receiving the arguments as references. These parameters finish borrowing when the function has completed, so borrowing works pretty well here. I’m defined the liberator parameter as mutable so we can add extra exclamation marks to the name. ! End results: same results as before. This is just a refactor.

Slide 63

Slide 63 text

struct Liberator { name: ~str, favorite_color: &'static str } ! impl Liberator { fn defiance(&mut self, prefix: &str) { self.name.push_char('!'); println!("{:s} {:s}", prefix, self.name); } } ! fn main() { let mut spartacus = ~Liberator { name: ~"Spartacus", favorite_color: "chartreuse" }; spartacus.defiance("I'm"); ! let mut wonder_woman = spartacus; wonder_woman.defiance("No I'm"); ! let mut hulk = wonder_woman; hulk.defiance("NO! HULK IS"); } I'm Spartacus!! No I'm Spartacus!!! NO! HULK IS Spartacus!!! Defiance really feels like it should be a method of Liberator. We can do that by declaring an impl block for Liberator. ! When calling defiance we just pass one argument now, covering the preamble (“I’m”, “No I’m”, “NO! HULK IS”, etc). ! When we define the function we have to declare an extra parameter called ‘self’. This is used to self reference inside the function. Kind of similar to how python declares methods. One difference with python though, you add modifiers like &, ~ and mut depending on how self is being accessed. In this we going with &mut so we can can change the name property.

Slide 64

Slide 64 text

struct Liberator { name: ~str, favorite_color: &'static str } ! impl Liberator { fn defiance(&mut self, prefix: &str) { self.name.push_char('!'); println!("{:s} {:s}", prefix, self.name); } } ! fn main() { let mut spartacus = ~Liberator { name: ~"Spartacus", favorite_color: "chartreuse" }; spartacus.defiance("I'm"); ! let mut wonder_woman = spartacus; wonder_woman.defiance("No I'm"); ! let mut hulk = wonder_woman; hulk.defiance("NO! HULK IS"); } I'm Spartacus!! No I'm Spartacus!!! NO! HULK IS Spartacus!!! Defiance really feels like it should be a method of Liberator. We can do that by declaring an impl block for Liberator. ! When calling defiance we just pass one argument now, covering the preamble (“I’m”, “No I’m”, “NO! HULK IS”, etc). ! When we define the function we have to declare an extra parameter called ‘self’. This is used to self reference inside the function. Kind of similar to how python declares methods. One difference with python though, you add modifiers like &, ~ and mut depending on how self is being accessed. In this we going with &mut so we can can change the name property.

Slide 65

Slide 65 text

struct Liberator { name: ~str, favorite_color: &'static str } ! impl Liberator { fn defiance(&mut self, prefix: &str) { self.name.push_char('!'); println!("{:s} {:s}", prefix, self.name); } } ! fn main() { let mut spartacus = ~Liberator { name: ~"Spartacus", favorite_color: "chartreuse" }; spartacus.defiance("I'm"); ! let mut wonder_woman = spartacus; wonder_woman.defiance("No I'm"); ! let mut hulk = wonder_woman; hulk.defiance("NO! HULK IS"); } I'm Spartacus!! No I'm Spartacus!!! NO! HULK IS Spartacus!!! Defiance really feels like it should be a method of Liberator. We can do that by declaring an impl block for Liberator. ! When calling defiance we just pass one argument now, covering the preamble (“I’m”, “No I’m”, “NO! HULK IS”, etc). ! When we define the function we have to declare an extra parameter called ‘self’. This is used to self reference inside the function. Kind of similar to how python declares methods. One difference with python though, you add modifiers like &, ~ and mut depending on how self is being accessed. In this we going with &mut so we can can change the name property.

Slide 66

Slide 66 text

So… how about we do something useful with shared pointers? How about Linked Lists? For a pacman game…

Slide 67

Slide 67 text

enum List{ Node(~str, ~List), Nada } ! fn main() { let blinky = ~Node(~"Blinky", ~Nada); let ghosts = blinky; } Building a Linked List We start off with a list of 1 item, the ghost Blinky. Blinky is defined using the Node tuple for defining a ghost name, and linking to the next link item.

Slide 68

Slide 68 text

enum List{ Node(~str, ~List), Nada } ! fn main() { let blinky = ~Node(~"Blinky", ~Nada); let ghosts = blinky; } Building a Linked List Our list is terminated with a List token we created named “Nada”. There is no concept of null in Rust. Nulls lead to bugs so Rust stays clear of them. ! Notice how Node and Nada are stored in a List enum? Enums in Rust are not limited to tokens or tokens with integer values. Our List enum used for each ghost gives us a choice between a Node tuple or a Nada terminator token.

Slide 69

Slide 69 text

enum List{ Node(~str, ~List), Nada } ! impl List { fn announce(~self) { match *self { Node(name, next) => { println!("{:s} has entered the game!", name); next.announce(); }, Nada => () } } } ! fn main() { let blinky = ~Node(~"Blinky", ~Nada); let ghosts = blinky; ! ghosts.announce(); } Building a Linked List Might be nice if we did something with this linked list, so lets implement a method called announce() that shows which ghosts have entered the game. Yes, use of ‘impl' is not limited to just structs! ! Our announce method is going to be called recursively. Borrowing isn’t going to be practical for this example, so we’ll define self as an owned pointer. ! We need to access values in our List enum. For that we’ll use ‘match’ which is a bit like a case statement. When matching value ‘self’ points to (dereferenced using ‘*’ before self), we need entries that cover all enum variants.

Slide 70

Slide 70 text

enum List{ Node(~str, ~List), Nada } ! impl List { fn announce(~self) { match *self { Node(name, next) => { println!("{:s} has entered the game!", name); next.announce(); }, Nada => () } } } ! fn main() { let blinky = ~Node(~"Blinky", ~Nada); let ghosts = blinky; ! ghosts.announce(); } Blinky has entered the game! Building a Linked List Might be nice if we did something with this linked list, so lets implement a method called announce() that shows which ghosts have entered the game. Yes, use of ‘impl' is not limited to just structs! ! Our announce method is going to be called recursively. Borrowing isn’t going to be practical for this example, so we’ll define self as an owned pointer. ! We need to access values in our List enum. For that we’ll use ‘match’ which is a bit like a case statement. When matching value ‘self’ points to (dereferenced using ‘*’ before self), we need entries that cover all enum variants.

Slide 71

Slide 71 text

enum List{ Node(~str, ~List), Nada } ! impl List { fn announce(~self) { match *self { Node(name, next) => { println!("{:s} has entered the game!", name); next.announce(); }, Nada => () } } } ! fn main() { let blinky = ~Node(~"Blinky", ~Nada); let pinky = ~Node(~"Pinky", blinky); let ghosts = pinky; ! ghosts.announce(); } We can now Pinky to the linked list.

Slide 72

Slide 72 text

enum List{ Node(~str, ~List), Nada } ! impl List { fn announce(~self) { match *self { Node(name, next) => { println!("{:s} has entered the game!", name); next.announce(); }, Nada => () } } } ! fn main() { let blinky = ~Node(~"Blinky", ~Nada); let pinky = ~Node(~"Pinky", blinky); let ghosts = pinky; ! ghosts.announce(); } We’ve tethered Pinky to Blinky like so…

Slide 73

Slide 73 text

enum List{ Node(~str, ~List), Nada } ! impl List { fn announce(~self) { match *self { Node(name, next) => { println!("{:s} has entered the game!", name); next.announce(); }, Nada => () } } } ! fn main() { let blinky = ~Node(~"Blinky", ~Nada); let pinky = ~Node(~"Pinky", blinky); let ghosts = pinky; ! ghosts.announce(); } We’ve tethered Pinky to Blinky like so…

Slide 74

Slide 74 text

enum List{ Node(~str, ~List), Nada } ! impl List { fn announce(~self) { match *self { Node(name, next) => { println!("{:s} has entered the game!", name); next.announce(); }, Nada => () } } } ! fn main() { let blinky = ~Node(~"Blinky", ~Nada); let pinky = ~Node(~"Pinky", blinky); let ghosts = pinky; ! ghosts.announce(); } Pinky has entered the game! Blinky has entered the game! We’ve tethered Pinky to Blinky like so…

Slide 75

Slide 75 text

enum List{ Node(~str, ~List), Nada } ! impl List { fn announce(~self) { match *self { Node(name, next) => { println!("{:s} has entered the game!", name); next.announce(); }, Nada => () } } } ! fn main() { let blinky = ~Node(~"Blinky", ~Nada); let pinky = ~Node(~"Pinky", blinky); let inky = ~Node(~"Inky", pinky); let clyde = ~Node(~"Clyde", inky); let ghosts = clyde; ! ghosts.announce(); } And so on…

Slide 76

Slide 76 text

enum List{ Node(~str, ~List), Nada } ! impl List { fn announce(~self) { match *self { Node(name, next) => { println!("{:s} has entered the game!", name); next.announce(); }, Nada => () } } } ! fn main() { let blinky = ~Node(~"Blinky", ~Nada); let pinky = ~Node(~"Pinky", blinky); let inky = ~Node(~"Inky", pinky); let clyde = ~Node(~"Clyde", inky); let ghosts = clyde; ! ghosts.announce(); } And so on…

Slide 77

Slide 77 text

enum List{ Node(~str, ~List), Nada } ! impl List { fn announce(~self) { match *self { Node(name, next) => { println!("{:s} has entered the game!", name); next.announce(); }, Nada => () } } } ! fn main() { let blinky = ~Node(~"Blinky", ~Nada); let pinky = ~Node(~"Pinky", blinky); let inky = ~Node(~"Inky", pinky); let clyde = ~Node(~"Clyde", inky); let ghosts = clyde; ! ghosts.announce(); } Clyde has entered the game! Inky has entered the game! Pinky has entered the game! Blinky has entered the game! And so on…

Slide 78

Slide 78 text

Rust has Generics. Also Traits. Let’s discuss further (I really want that mug…)

Slide 79

Slide 79 text

struct Wotsit { item: int, description: ~str } ! impl Wotsit { fn examine(&self) { println!("This wotsit is brought to you by the number {:d}", self.item); } } ! fn main() { let the_answer = Wotsit { item: 42, description: ~"The ultimate answer"}; the_answer.examine(); } Generics and Traits Starting off, I’ve got a wotsit that can hold integer values. But I’d really like it to be able to contain anything.

Slide 80

Slide 80 text

struct Wotsit { item: int, description: ~str } ! impl Wotsit { fn examine(&self) { println!("This wotsit is brought to you by the number {:d}", self.item); } } ! fn main() { let the_answer = Wotsit { item: 42, description: ~"The ultimate answer"}; the_answer.examine(); } This wotsit is brought to you by the number 42 Generics and Traits Starting off, I’ve got a wotsit that can hold integer values. But I’d really like it to be able to contain anything.

Slide 81

Slide 81 text

struct Wotsit { item: T, description: ~str } ! impl Wotsit { fn examine(&self) { println!("This wotsit is brought to you by the number {:d}", self.item); } } ! fn main() { let the_answer = Wotsit { item: 42, description: ~"The ultimate answer"}; the_answer.examine(); } Generics and Traits So I’ve tweaked the struct a little so it accepts any type. A consequence of this Wotsit implementation is no longer good. Its designed to work for integers only

Slide 82

Slide 82 text

struct Wotsit { item: T, description: ~str } ! trait Viewable{ fn examine(&self) { println!("This wotsit contains... you know... thingies"); } } ! impl Viewable for Wotsit {} ! fn main() { let the_answer = Wotsit { item: 42, description: ~"The ultimate answer"}; the_answer.examine(); } Generics and Traits So to deal with these type specific implementations I’ve implemented a trait called Viewable. Viewable is defined now for integer Wotsits. I’ve given Viewable a default for the examine function that prints this really vague piece of information…

Slide 83

Slide 83 text

struct Wotsit { item: T, description: ~str } ! trait Viewable{ fn examine(&self) { println!("This wotsit contains... you know... thingies"); } } ! impl Viewable for Wotsit {} ! fn main() { let the_answer = Wotsit { item: 42, description: ~"The ultimate answer"}; the_answer.examine(); } Generics and Traits So to deal with these type specific implementations I’ve implemented a trait called Viewable. Viewable is defined now for integer Wotsits. I’ve given Viewable a default for the examine function that prints this really vague piece of information…

Slide 84

Slide 84 text

struct Wotsit { item: T, description: ~str } ! trait Viewable{ fn examine(&self) { println!("This wotsit contains... you know... thingies"); } } ! impl Viewable for Wotsit {} ! fn main() { let the_answer = Wotsit { item: 42, description: ~"The ultimate answer"}; the_answer.examine(); } This wotsit contains... you know... thingies Generics and Traits So to deal with these type specific implementations I’ve implemented a trait called Viewable. Viewable is defined now for integer Wotsits. I’ve given Viewable a default for the examine function that prints this really vague piece of information…

Slide 85

Slide 85 text

struct Wotsit { item: T, description: ~str } ! trait Viewable{ fn examine(&self) { println!("This wotsit contains... you know... thingies"); } } ! impl Viewable for Wotsit { fn examine(&self) { println!("This wotsit is brought to you by the number {:d}", self.item); } } ! fn main() { let the_answer = Wotsit { item: 42, description: ~"The ultimate answer"}; the_answer.examine(); } That’s a bit too vague, so we’ll just override the examine method for integer Wotsits.

Slide 86

Slide 86 text

struct Wotsit { item: T, description: ~str } ! trait Viewable{ fn examine(&self) { println!("This wotsit contains... you know... thingies"); } } ! impl Viewable for Wotsit { fn examine(&self) { println!("This wotsit is brought to you by the number {:d}", self.item); } } ! fn main() { let the_answer = Wotsit { item: 42, description: ~"The ultimate answer"}; the_answer.examine(); } This wotsit is brought to you by the number 42 That’s a bit too vague, so we’ll just override the examine method for integer Wotsits.

Slide 87

Slide 87 text

struct Wotsit { item: T, description: ~str } ! trait Viewable{ fn examine(&self) { println!("This wotsit contains... you know... thingies"); } } ! impl Viewable for Wotsit { fn examine(&self) { println!("This wotsit is brought to you by the number {:d}", self.item); } } ! impl Viewable for Wotsit<~str> { fn examine(&self) { println!("This wotsit contains a '{:s}'", self.item); } } ! fn main() { let the_answer = Wotsit { item: 42, description: ~"The ultimate answer"}; the_answer.examine(); let power_cell = Wotsit { item: ~"confused cat", description: ~"Powers the internet"}; power_cell.examine(); } … and lets expand it’s capabilities to actor for owned strings….

Slide 88

Slide 88 text

struct Wotsit { item: T, description: ~str } ! trait Viewable{ fn examine(&self) { println!("This wotsit contains... you know... thingies"); } } ! impl Viewable for Wotsit { fn examine(&self) { println!("This wotsit is brought to you by the number {:d}", self.item); } } ! impl Viewable for Wotsit<~str> { fn examine(&self) { println!("This wotsit contains a '{:s}'", self.item); } } ! fn main() { let the_answer = Wotsit { item: 42, description: ~"The ultimate answer"}; the_answer.examine(); let power_cell = Wotsit { item: ~"confused cat", description: ~"Powers the internet"}; power_cell.examine(); } … and lets expand it’s capabilities to actor for owned strings….

Slide 89

Slide 89 text

struct Wotsit { item: T, description: ~str } ! trait Viewable{ fn examine(&self) { println!("This wotsit contains... you know... thingies"); } } ! impl Viewable for Wotsit { fn examine(&self) { println!("This wotsit is brought to you by the number {:d}", self.item); } } ! impl Viewable for Wotsit<~str> { fn examine(&self) { println!("This wotsit contains a '{:s}'", self.item); } } ! fn main() { let the_answer = Wotsit { item: 42, description: ~"The ultimate answer"}; the_answer.examine(); let power_cell = Wotsit { item: ~"confused cat", description: ~"Powers the internet"}; power_cell.examine(); } … and lets expand it’s capabilities to actor for owned strings….

Slide 90

Slide 90 text

struct Wotsit { item: T, description: ~str } ! trait Viewable{ fn examine(&self) { println!("This wotsit contains... you know... thingies"); } } ! impl Viewable for Wotsit { fn examine(&self) { println!("This wotsit is brought to you by the number {:d}", self.item); } } ! impl Viewable for Wotsit<~str> { fn examine(&self) { println!("This wotsit contains a '{:s}'", self.item); } } ! fn main() { let the_answer = Wotsit { item: 42, description: ~"The ultimate answer"}; the_answer.examine(); let power_cell = Wotsit { item: ~"confused cat", description: ~"Powers the internet"}; power_cell.examine(); } This wotsit is brought to you by the number 42 This wotsit contains a 'confused cat' … and lets expand it’s capabilities to actor for owned strings….

Slide 91

Slide 91 text

Let’s talk about closures!

Slide 92

Slide 92 text

fn(x) { ! } counter += counter + x; 1) For those of you need reminding, a closure is typically an anonymous function that 2) can influence variable outside of it’s internal scope. So that each time you call it can change state. So in this example the closure is updating the outer counter variable each time it gets run. 3) Although of course closures in Rust look more like ruby blocks.

Slide 93

Slide 93 text

fn(x) { ! } Function Scope Outer let mut counter = 4; counter += counter + x; 1) For those of you need reminding, a closure is typically an anonymous function that 2) can influence variable outside of it’s internal scope. So that each time you call it can change state. So in this example the closure is updating the outer counter variable each time it gets run. 3) Although of course closures in Rust look more like ruby blocks.

Slide 94

Slide 94 text

Function Scope Outer let mut counter = 4; counter += counter + x; let closure = |x| { ! } ! ! 1) For those of you need reminding, a closure is typically an anonymous function that 2) can influence variable outside of it’s internal scope. So that each time you call it can change state. So in this example the closure is updating the outer counter variable each time it gets run. 3) Although of course closures in Rust look more like ruby blocks.

Slide 95

Slide 95 text

use std::io::timer::sleep; ! // Rust 0.10 fn main() { let actions = [("Captain America", "bashes", 20), ("Black Widow", "slashes", 25), ("Ironman", "throws cash at", 0), ("Hulk", "SMASHES", 200)]; ! let mut outcomes = actions.iter() .map(|&action| { let (hero, attack, damage) = action; format!("{:s} {:s} Red Skull for {:d} damage", hero, attack, damage) }); ! for outcome in outcomes { spawn(proc() { sleep(500); println!(“{:s}”, outcome); }); } } Closures As you may remember we used closures earlier in the map function for building outcome strings. And also for spawning tasks.

Slide 96

Slide 96 text

use std::io::timer::sleep; ! // Rust 0.10 fn main() { let actions = [("Captain America", "bashes", 20), ("Black Widow", "slashes", 25), ("Ironman", "throws cash at", 0), ("Hulk", "SMASHES", 200)]; ! let mut outcomes = actions.iter() .map(|&action| { let (hero, attack, damage) = action; format!("{:s} {:s} Red Skull for {:d} damage", hero, attack, damage) }); ! for outcome in outcomes { spawn(proc() { sleep(500); println!(“{:s}”, outcome); }); } } Closures As you may remember we used closures earlier in the map function for building outcome strings. And also for spawning tasks.

Slide 97

Slide 97 text

fn main() { let mut outer_value = 0; ! let increment = |x| { outer_value += x; }; ! increment(7); println!("outer_value now equals {:d}", outer_value); } Closures Let’s play! To start off with we have a simple “increment” closure. We execute it with an argument of 7. Inside the closure x will equal 7, which gets added onto the outer_value.

Slide 98

Slide 98 text

fn main() { let mut outer_value = 0; ! let increment = |x| { outer_value += x; }; ! increment(7); println!("outer_value now equals {:d}", outer_value); } outer_value now equals 7 Closures Let’s play! To start off with we have a simple “increment” closure. We execute it with an argument of 7. Inside the closure x will equal 7, which gets added onto the outer_value.

Slide 99

Slide 99 text

fn transform(mutator: |int| -> int, value: int ) { let result = mutator(value); println!("outer_value now equals {:d}", result); } ! fn main() { let mut outer_value = 0; ! let increment = |x| { outer_value += x; outer_value }; ! transform(increment, 7); } outer_value now equals 7 We can pass this closure into a function as an argument. Transform will accept any closure along with an input value. So again we get 7.

Slide 100

Slide 100 text

fn transform(mutator: |int| -> int, value: int ) { let result = mutator(value); println!("outer_value now equals {:d}", result); } ! fn main() { let mut outer_value = 0; ! let increment = |x| { outer_value += x; outer_value }; ! transform(increment, 7); transform(increment, 35); } As I mentioned earlier, we expect closures to continually change state. Lets test that by transforming increment twice. This doesn’t go so well. The problem is we passed the closure as what’s known as a stack closure. A closure defined on the stack. When it got passed in the ownership moved making it unsafe to call twice. The compiler has actually told us how to work around this though. We just have to wrap the stack closure in another closure as we call it.

Slide 101

Slide 101 text

fn transform(mutator: |int| -> int, value: int ) { let result = mutator(value); println!("outer_value now equals {:d}", result); } ! fn main() { let mut outer_value = 0; ! let increment = |x| { outer_value += x; outer_value }; ! transform(increment, 7); transform(increment, 35); } closures.rs:15:15: 15:24 error: use of moved value: `increment` closures.rs:15 transform(increment, 35); ^~~~~~~~~ closures.rs:14:15: 14:24 note: `increment` moved here because it has type `|int| -> int`, which is a non-copyable stack closure (capture it in a new closure, e.g. `|x| f(x)`, to override) closures.rs:14 transform(increment, 7); ^~~~~~~~~ As I mentioned earlier, we expect closures to continually change state. Lets test that by transforming increment twice. This doesn’t go so well. The problem is we passed the closure as what’s known as a stack closure. A closure defined on the stack. When it got passed in the ownership moved making it unsafe to call twice. The compiler has actually told us how to work around this though. We just have to wrap the stack closure in another closure as we call it.

Slide 102

Slide 102 text

fn transform(mutator: |int| -> int, value: int ) { let result = mutator(value); println!("outer_value now equals {:d}", result); } ! fn main() { let mut outer_value = 0; ! let increment = |x| { outer_value += x; outer_value }; ! transform(|x| { increment(x) }, 7); transform(|x| { increment(x) }, 35); } So now we wrap increment in a closure as we call transform and all is good!

Slide 103

Slide 103 text

fn transform(mutator: |int| -> int, value: int ) { let result = mutator(value); println!("outer_value now equals {:d}", result); } ! fn main() { let mut outer_value = 0; ! let increment = |x| { outer_value += x; outer_value }; ! transform(|x| { increment(x) }, 7); transform(|x| { increment(x) }, 35); } outer_value now equals 7 outer_value now equals 42 So now we wrap increment in a closure as we call transform and all is good!

Slide 104

Slide 104 text

fn transform(mutator: |int| -> int, value: int ) { let result = mutator(value); println!(“ outer_value now equals {:d}", result); } ! fn main() { let mut outer_inc_value = 0; let increment = |x| { outer_inc_value += x; outer_inc_value }; ! let mut outer_mul_value = 1; let multiply = |x| { outer_mul_value *= x; outer_mul_value }; ! println!(“Additions:"); transform(|x| { increment(x) }, 7); transform(|x| { increment(x) }, 35); ! println!(“Multiplications:"); transform(|x| { multiply(x) }, 5); transform(|x| { multiply(x) }, 3); } I mentioned earlier that transform can take any closure as an input, as long as it contains a single argument. So lets try a multiplication closure…

Slide 105

Slide 105 text

fn transform(mutator: |int| -> int, value: int ) { let result = mutator(value); println!(“ outer_value now equals {:d}", result); } ! fn main() { let mut outer_inc_value = 0; let increment = |x| { outer_inc_value += x; outer_inc_value }; ! let mut outer_mul_value = 1; let multiply = |x| { outer_mul_value *= x; outer_mul_value }; ! println!(“Additions:"); transform(|x| { increment(x) }, 7); transform(|x| { increment(x) }, 35); ! println!(“Multiplications:"); transform(|x| { multiply(x) }, 5); transform(|x| { multiply(x) }, 3); } Additions: outer_value now equals 7 outer_value now equals 42 Multiplications: outer_value now equals 5 outer_value now equals 15 I mentioned earlier that transform can take any closure as an input, as long as it contains a single argument. So lets try a multiplication closure…

Slide 106

Slide 106 text

Rust is really good at concurrency. Lets talk about that!

Slide 107

Slide 107 text

Rust is really good at concurrency. Lets talk about that!

Slide 108

Slide 108 text

Tasks Rust use the CSP model of concurrency. Which stands for Communicating Sequential Processes. Using this model rust threads (know as Tasks) can communicate through channels or pipes. When creating the simplest variant, a “Sender” variable is provided for sending information and a “Port” variable is provided for receiving. So now Task A can send a message to Task B asynchronously despite being on a different thread. Task B listens by calling the recv() method. Task A sends data using the send() method. The companion cube has just sent a little heart to the cake. ! This is just the simplest type of channel. For example duplex channels are also available in Rust for sending both ways.

Slide 109

Slide 109 text

Tasks CSP - Communicating Sequential Processes Rust use the CSP model of concurrency. Which stands for Communicating Sequential Processes. Using this model rust threads (know as Tasks) can communicate through channels or pipes. When creating the simplest variant, a “Sender” variable is provided for sending information and a “Port” variable is provided for receiving. So now Task A can send a message to Task B asynchronously despite being on a different thread. Task B listens by calling the recv() method. Task A sends data using the send() method. The companion cube has just sent a little heart to the cake. ! This is just the simplest type of channel. For example duplex channels are also available in Rust for sending both ways.

Slide 110

Slide 110 text

Tasks CSP - Communicating Sequential Processes Rust use the CSP model of concurrency. Which stands for Communicating Sequential Processes. Using this model rust threads (know as Tasks) can communicate through channels or pipes. When creating the simplest variant, a “Sender” variable is provided for sending information and a “Port” variable is provided for receiving. So now Task A can send a message to Task B asynchronously despite being on a different thread. Task B listens by calling the recv() method. Task A sends data using the send() method. The companion cube has just sent a little heart to the cake. ! This is just the simplest type of channel. For example duplex channels are also available in Rust for sending both ways.

Slide 111

Slide 111 text

Tasks CSP - Communicating Sequential Processes Sender<~str> Task A Rust use the CSP model of concurrency. Which stands for Communicating Sequential Processes. Using this model rust threads (know as Tasks) can communicate through channels or pipes. When creating the simplest variant, a “Sender” variable is provided for sending information and a “Port” variable is provided for receiving. So now Task A can send a message to Task B asynchronously despite being on a different thread. Task B listens by calling the recv() method. Task A sends data using the send() method. The companion cube has just sent a little heart to the cake. ! This is just the simplest type of channel. For example duplex channels are also available in Rust for sending both ways.

Slide 112

Slide 112 text

Tasks CSP - Communicating Sequential Processes Sender<~str> Task A Receiver<~str> Task B Rust use the CSP model of concurrency. Which stands for Communicating Sequential Processes. Using this model rust threads (know as Tasks) can communicate through channels or pipes. When creating the simplest variant, a “Sender” variable is provided for sending information and a “Port” variable is provided for receiving. So now Task A can send a message to Task B asynchronously despite being on a different thread. Task B listens by calling the recv() method. Task A sends data using the send() method. The companion cube has just sent a little heart to the cake. ! This is just the simplest type of channel. For example duplex channels are also available in Rust for sending both ways.

Slide 113

Slide 113 text

Tasks println(receiver.recv()); CSP - Communicating Sequential Processes Sender<~str> Task A Receiver<~str> Task B Rust use the CSP model of concurrency. Which stands for Communicating Sequential Processes. Using this model rust threads (know as Tasks) can communicate through channels or pipes. When creating the simplest variant, a “Sender” variable is provided for sending information and a “Port” variable is provided for receiving. So now Task A can send a message to Task B asynchronously despite being on a different thread. Task B listens by calling the recv() method. Task A sends data using the send() method. The companion cube has just sent a little heart to the cake. ! This is just the simplest type of channel. For example duplex channels are also available in Rust for sending both ways.

Slide 114

Slide 114 text

Tasks sender.send(“<3!”); println(receiver.recv()); CSP - Communicating Sequential Processes Sender<~str> Task A Receiver<~str> Task B Rust use the CSP model of concurrency. Which stands for Communicating Sequential Processes. Using this model rust threads (know as Tasks) can communicate through channels or pipes. When creating the simplest variant, a “Sender” variable is provided for sending information and a “Port” variable is provided for receiving. So now Task A can send a message to Task B asynchronously despite being on a different thread. Task B listens by calling the recv() method. Task A sends data using the send() method. The companion cube has just sent a little heart to the cake. ! This is just the simplest type of channel. For example duplex channels are also available in Rust for sending both ways.

Slide 115

Slide 115 text

Tasks sender.send(“<3!”); println(receiver.recv()); <3! CSP - Communicating Sequential Processes Sender<~str> Task A Receiver<~str> Task B Rust use the CSP model of concurrency. Which stands for Communicating Sequential Processes. Using this model rust threads (know as Tasks) can communicate through channels or pipes. When creating the simplest variant, a “Sender” variable is provided for sending information and a “Port” variable is provided for receiving. So now Task A can send a message to Task B asynchronously despite being on a different thread. Task B listens by calling the recv() method. Task A sends data using the send() method. The companion cube has just sent a little heart to the cake. ! This is just the simplest type of channel. For example duplex channels are also available in Rust for sending both ways.

Slide 116

Slide 116 text

fn main() { let (sender, receiver) = channel(); ! spawn(proc() { sender.send("Marco!"); sender.send("Pollooo!"); }); ! for _ in range(0, 2) { println!("Got back '{}' from a rust task!”, receiver.recv()); } } Tasks So here’s something similar as actual code. In this case we just have a single task created through the spawn function. It contains a proc, an owned closure. Because it’s a closure it can still access the chan variable despite it being in the other scope. In this case we’re just receiving the messages from the outer function itself. Note that in the for loop I didn’t actually need the variable containing the range number, so I just put in an underscore as a placeholder so we don’t get unused local variable warnings. Let’s add more tasks.

Slide 117

Slide 117 text

fn main() { let (sender, receiver) = channel(); ! spawn(proc() { sender.send("Marco!"); sender.send("Pollooo!"); }); ! for _ in range(0, 2) { println!("Got back '{}' from a rust task!”, receiver.recv()); } } Got back 'Marco!' from a rust task! Got back 'Pollooo!' from a rust task! Tasks So here’s something similar as actual code. In this case we just have a single task created through the spawn function. It contains a proc, an owned closure. Because it’s a closure it can still access the chan variable despite it being in the other scope. In this case we’re just receiving the messages from the outer function itself. Note that in the for loop I didn’t actually need the variable containing the range number, so I just put in an underscore as a placeholder so we don’t get unused local variable warnings. Let’s add more tasks.

Slide 118

Slide 118 text

use std::ascii::StrAsciiExt; ! fn main() { let (sender1, receiver1) = channel(); let (sender2, receiver2) = channel(); ! sender1.send(~"ycnerrucnoc hcus"); sender1.send(~"wow secar ynam"); sender1.send(~""); ! spawn(proc() { loop { let code = receiver1.recv(); sender2.send(code.to_ascii_upper()); if code == ~"" { break; } } }); ! spawn(proc() { loop { let code = receiver2.recv(); if code == ~"" { break; } else { println!("Got back '{:s}'", code); } } }); } In this sample we’re communicating directly between to tasks using 2 different channels. We use the first channel to send 2 encrypted messages to the first tasks which converts it to upper case and then relays it to a second task to output the results. For this setup you can keep sending messages until a blank message is received at which point the listeners quit. I’ve implemented the repeat until using loops and breaks. Most of the code inside the first task is just reading and writing to the channel. We could refactor that into another function.

Slide 119

Slide 119 text

use std::ascii::StrAsciiExt; ! fn main() { let (sender1, receiver1) = channel(); let (sender2, receiver2) = channel(); ! sender1.send(~"ycnerrucnoc hcus"); sender1.send(~"wow secar ynam"); sender1.send(~""); ! spawn(proc() { loop { let code = receiver1.recv(); sender2.send(code.to_ascii_upper()); if code == ~"" { break; } } }); ! spawn(proc() { loop { let code = receiver2.recv(); if code == ~"" { break; } else { println!("Got back '{:s}'", code); } } }); } Got back 'YCNERRUCNOC HCUS' Got back 'WOW SECAR YNAM' In this sample we’re communicating directly between to tasks using 2 different channels. We use the first channel to send 2 encrypted messages to the first tasks which converts it to upper case and then relays it to a second task to output the results. For this setup you can keep sending messages until a blank message is received at which point the listeners quit. I’ve implemented the repeat until using loops and breaks. Most of the code inside the first task is just reading and writing to the channel. We could refactor that into another function.

Slide 120

Slide 120 text

fn main() { ... ! spawn(proc() { loop { let code = receiver1.recv(); sender2.send(code.to_ascii_upper()); if code == ~""{ break; } } }); ! spawn(proc() { loop { let code = receiver2.recv(); let s: ~str = code.chars_rev().collect(); sender3.send(s); if code == ~""{ break; } } }); ! spawn(proc() { loop { let code = receiver3.recv(); if code == ~""{ break; } else { println!("Got back ‘{:s}’", code); } } }); ! ... } One refactoring later, I’ve got a function called transform for manage the channels. Just tell tell transform which port you’re listening on, which channel you’re sending on and a closure for doing the actual transformation. In this case the closure just contains instructions to convert the strings to upper case. This will make it really easy to add a 3rd task.

Slide 121

Slide 121 text

fn main() { ... ! spawn(proc() { loop { let code = receiver1.recv(); sender2.send(code.to_ascii_upper()); if code == ~""{ break; } } }); ! spawn(proc() { loop { let code = receiver2.recv(); let s: ~str = code.chars_rev().collect(); sender3.send(s); if code == ~""{ break; } } }); ! spawn(proc() { loop { let code = receiver3.recv(); if code == ~""{ break; } else { println!("Got back ‘{:s}’", code); } } }); ! ... } One refactoring later, I’ve got a function called transform for manage the channels. Just tell tell transform which port you’re listening on, which channel you’re sending on and a closure for doing the actual transformation. In this case the closure just contains instructions to convert the strings to upper case. This will make it really easy to add a 3rd task.

Slide 122

Slide 122 text

fn main() { ... ! spawn(proc() { loop { let code = receiver1.recv(); sender2.send(code.to_ascii_upper()); if code == ~""{ break; } } }); ! spawn(proc() { loop { let code = receiver2.recv(); let s: ~str = code.chars_rev().collect(); sender3.send(s); if code == ~""{ break; } } }); ! spawn(proc() { loop { let code = receiver3.recv(); if code == ~""{ break; } else { println!("Got back ‘{:s}’", code); } } }); ! ... } One refactoring later, I’ve got a function called transform for manage the channels. Just tell tell transform which port you’re listening on, which channel you’re sending on and a closure for doing the actual transformation. In this case the closure just contains instructions to convert the strings to upper case. This will make it really easy to add a 3rd task.

Slide 123

Slide 123 text

fn transform(receiver: Receiver<~str>, sender: Sender<~str>, transformation: |~str| -> ~str) { loop { let code = receiver.recv(); if code == ~"" { sender.send(~""); break; } sender.send( transformation(code) ); } } ! fn main() { ... ! spawn(proc() { transform(receiver1, sender2, |x: ~str| x.to_ascii_upper()); }); ! spawn(proc() { transform(receiver2, sender3, |x: ~str| x.chars_rev().collect()); }); ! spawn(proc() { loop { let code = receiver3.recv(); if code == ~"" { break; } else { println!("Got back '{:s}'", code); } } }); } So now I’m adding a 3rd task and a 3rd channel to reverse the order of the text characters. Giving us…

Slide 124

Slide 124 text

fn transform(receiver: Receiver<~str>, sender: Sender<~str>, transformation: |~str| -> ~str) { loop { let code = receiver.recv(); if code == ~"" { sender.send(~""); break; } sender.send( transformation(code) ); } } ! fn main() { ... ! spawn(proc() { transform(receiver1, sender2, |x: ~str| x.to_ascii_upper()); }); ! spawn(proc() { transform(receiver2, sender3, |x: ~str| x.chars_rev().collect()); }); ! spawn(proc() { loop { let code = receiver3.recv(); if code == ~"" { break; } else { println!("Got back '{:s}'", code); } } }); } So now I’m adding a 3rd task and a 3rd channel to reverse the order of the text characters. Giving us…

Slide 125

Slide 125 text

fn transform(receiver: Receiver<~str>, sender: Sender<~str>, transformation: |~str| -> ~str) { loop { let code = receiver.recv(); if code == ~"" { sender.send(~""); break; } sender.send( transformation(code) ); } } ! fn main() { ... ! spawn(proc() { transform(receiver1, sender2, |x: ~str| x.to_ascii_upper()); }); ! spawn(proc() { transform(receiver2, sender3, |x: ~str| x.chars_rev().collect()); }); ! spawn(proc() { loop { let code = receiver3.recv(); if code == ~"" { break; } else { println!("Got back '{:s}'", code); } } }); } So now I’m adding a 3rd task and a 3rd channel to reverse the order of the text characters. Giving us…

Slide 126

Slide 126 text

use std::ascii::StrAsciiExt; ! fn transform(receiver: Receiver<~str>, sender: Sender<~str>, transformation: |~str| -> ~str) { loop { let code = receiver.recv(); if code == ~"" { sender.send(~""); break; } sender.send( transformation(code) ); } } ! fn main() { let (sender1, receiver1) = channel(); let (sender2, receiver2) = channel(); let (sender3, receiver3) = channel(); ! sender1.send(~"ycnerrucnoc hcus"); sender1.send(~"wow secar ynam"); sender1.send(~""); ! spawn(proc() { transform(receiver1, sender2, |x: ~str| x.to_ascii_upper()); }); ! spawn(proc() { transform(receiver2, sender3, |x: ~str| x.chars_rev().collect()); }); ! spawn(proc() { loop { let code = receiver3.recv(); if code == ~"" { break; } else { println!("Got back '{:s}'", code); } } }); } So now I’m adding a 3rd task and a 3rd channel to reverse the order of the text characters. Giving us…

Slide 127

Slide 127 text

use std::ascii::StrAsciiExt; ! fn transform(receiver: Receiver<~str>, sender: Sender<~str>, transformation: |~str| -> ~str) { loop { let code = receiver.recv(); if code == ~"" { sender.send(~""); break; } sender.send( transformation(code) ); } } ! fn main() { let (sender1, receiver1) = channel(); let (sender2, receiver2) = channel(); let (sender3, receiver3) = channel(); ! sender1.send(~"ycnerrucnoc hcus"); sender1.send(~"wow secar ynam"); sender1.send(~""); ! spawn(proc() { transform(receiver1, sender2, |x: ~str| x.to_ascii_upper()); }); ! spawn(proc() { transform(receiver2, sender3, |x: ~str| x.chars_rev().collect()); }); ! spawn(proc() { loop { let code = receiver3.recv(); if code == ~"" { break; } else { println!("Got back '{:s}'", code); } } }); } Got back 'SUCH CONCURRENCY' Got back 'MANY RACES WOW' So now I’m adding a 3rd task and a 3rd channel to reverse the order of the text characters. Giving us…

Slide 128

Slide 128 text

youtube: The Rust language: memory, ownership and lifetimes - Nicholas Matsakis ! http://rustforrubyists.com ! http://rust-lang.org gregmalcolm Learning Resources speakerdeck: gregmalcolm/rust-me-im-a-developer For those of you interested in looking into rust, at the moment, my suggestion for getting started is to look on youtube for a talk by Nicholas Matsakis which gives a really nice grounding on Rust. Next off I highly recommend Steve Klabnik’s Rust for Rubyists tutorial, even if you don’t know Ruby. It’s a lot easier to follow the current official tutorial. Next off look around the rust website for more.