Learn Rust Programming - Complete Course 🦀

The Power of Iterators in Rust: A Comprehensive Guide

=====================================================

Iterators are a fundamental concept in Rust programming, allowing developers to work with collections in a safe and efficient manner. In this article, we will delve into the world of iterators in Rust, exploring their features, benefits, and best practices.

**Defining an Iterator**

An iterator is a data structure that allows us to traverse through the elements of a collection one by one. In Rust, iterators are defined using the `Iterator` trait, which provides a set of methods for iterating over a collection. We can define our own custom iterators or use existing ones provided by the Rust standard library.

Here's an example of defining a simple iterator:

```rust

struct MyIterator {

data: Vec,

}

impl Iterator for MyIterator {

type Item = i32;

fn next(&mut self) -> Option {

if self.data.is_empty() {

None

} else {

let value = self.data.pop();

Some(*value)

}

}

}

```

This iterator takes ownership of a vector `data` and provides an `Option` value for each iteration, allowing us to consume the elements one by one.

**Using Iterators**

Iterators are incredibly useful in Rust programming. We can use them to work with collections in a safe and efficient manner. For example, we can iterate over a vector using the `iter()` method:

```rust

let data = vec![1, 2, 3];

for value in &data {

println!("{}", value);

}

```

This code prints each element of the vector to the console.

**Consuming Iterators**

When working with iterators, we often need to consume the elements one by one. We can do this using the `collect()` method:

```rust

let data = vec![1, 2, 3];

let result: Vec = data.into_iter().collect();

println!("{:?}", result); // prints [1, 2, 3]

```

In this example, we use the `into_iter()` method to convert the vector into an iterator and then collect the elements into a new vector.

**Iterator Adapters**

Iterator adapters are a powerful feature in Rust that allows us to transform iterators in various ways. We can create custom iterator adapters using traits like `Map`, `Filter`, and `FlatMap`. Here's an example of defining a map iterator adapter:

```rust

struct MapMyIterator {

data: Vec,

}

impl Iterator for MapMyIterator {

type Item = i32;

fn next(&mut self) -> Option {

if self.data.is_empty() {

None

} else {

let value = self.data.pop();

Some(value * 2)

}

}

}

```

This iterator adapter multiplies each element of the original vector by 2.

**Chaining Iterator Adapters**

We can chain multiple iterator adapters to perform complex transformations on iterators. Here's an example:

```rust

let data = vec![1, 2, 3];

let result: Vec = data.into_iter()

.map(|value| value * 2)

.filter(|value| *value % 2 == 0)

.collect();

println!("{:?}", result); // prints [4]

```

In this example, we use the `map()` method to multiply each element by 2, then filter out odd values using the `filter()` method, and finally collect the elements into a new vector.

**Conclusion**

Iterators are a fundamental concept in Rust programming that allows us to work with collections in a safe and efficient manner. We can define our own custom iterators or use existing ones provided by the Rust standard library. Iterator adapters provide a powerful way to transform iterators, allowing us to perform complex transformations on iterators. By chaining multiple iterator adapters, we can achieve complex transformations in a readable and maintainable way.

Best practices for working with iterators include:

* Using `into_iter()` to convert collections into iterators

* Using `collect()` to consume iterators and collect elements into a new collection

* Defining custom iterator adapters using traits like `Map`, `Filter`, and `FlatMap`

* Chaining multiple iterator adapters to perform complex transformations on iterators

"WEBVTTKind: captionsLanguage: enwelcome to this rust programming course for beginners this comprehensive course will guide you step by step through the fundamentals of rust enabling you to build robust and efficient applications from understanding basic syntax and data types to exploring more advanced topics like lifetimes and closures arfan zubi will provide clear explanations and Hands-On exercises to ensure you grasp every concept effectively get ready to unlock the power of rust and elevate your programming skills to new heights hi my name is Irfan and I will be your guide in this comprehensive for us course for beginners so if you're interested in learning rust then congratulations you have found the right course so I've designed this course to ensure that you will learn rust the proper way using slides and visuals to illustrate Concepts and providing exercises we will solve together this means you will have an appropriate mix of theory and practice that will help you step by step mastering this new awesome programming language and I strongly advise you to code along because you will learn so much more when actively participating rather than just watching the video so let's Dive In so what are the goals of this course first and foremost getting familiar with Core Concepts and syntax of the language we will take a look at data types and data structures as well as ownership and borrowing we will see how data is allocated in memory and how we access the data and we'll take a look at standard Library generics trades lifetimes and much much more basically everything you need to become a great rust developer so why would you want to learn rust now there would be I guess a thousand reasons but I have here put together some of the main ones now rust is a really fast language in terms of execution time it provides a rich type system it doesn't have a garbage collector which is part of the reason why it's so fast it provides useful compiler outputs and you will see that in the exercises we are solving it guarantees memory safety and it's the most beloved programming language since 2016 according to stack overflow meaning now seven years in a row and we are experiencing fast adoption in various branches foreign now over here you can see a table which shows various programming languages and their score in terms of Energy Efficiency and execution time and as you can see rust is one of the languages on the top of the list now over here are some resources that you can learn with now I guess the main resource would be the RAS programming language really fantastic book and I recommend you to read it then there is rustlings these are small exercises basically programs which you have to debug then there is Ross by example so if your style is more focusing code and less on text then this would be suitable for you and there is a website called rust by practice and that is what we will cover together and if you want to go more advanced there is a book called Russ forestations and it's also very much recommended to eat it so before we dive into the first topic I want to show you the website of lost by practice and you can access it by going to the URL practice dot RS and this is the website and over here as you can see these are all the topics we will cover together right now this will be the first one variables and as you can see over here we have small exercises that we will solve together now before we start I want to show you something else and of course we'll keep up with the tradition and write a hello world program so you can go to the rustlang.org website this is the official website of the rust foundation and you can click over here playground and as you can see over here you can write rascode and then execute it and it's really cool because you even have the Vim key bindings over here and you can even choose themes like that and let's write our first program so every program in Rust starts in the main function this is the starting point of execution of every rust program and we declare functions with the FN keyword so to print out to standard output we can use the print line macro as you can see we write print Ln exclamation mark and then we can provide here the string we want to Output like that and then you can hit run and as you can see we have executed our first program so congratulations I hope you coded along and now we start with the actual course so variables variables are assigned using the LED keyword so if you're coming from a JavaScript background this might seem familiar you can print to standard output by the print or print Ln macros so print and print Ln are basically very much the same but print Ln adds a new line at the end of the output and scope of a variable is defined by the block of code in which it is declared now a function is a named block of code that is reusable and shadowing allows a variable to be redeclared in the same scope with the same name so let's solve some exercises binding and mutability a variable can be used only if it has been initialized fix the arrow below with least amount of modification to the code so again we are starting always in the main function now over here we have two variables and again we declare variables with the let keyword then the name and over here we have a type annotation now don't worry too much for now we'll cover that in the next topic to come but for now think of i32 as an integer type right and then we have here the assert EQ macro which stands for assert equality so this macro takes two arguments and just basically asserts that these two are equal in case they are not equal the program will Panic meaning immediate exiting and returning an error message if there are in fact equal then the program will just continue executing so as you can see we want here that X is equal to a value of 5 so we can initialize X over here with with a value of 5 like that and to run this program you can either click here or you can press Ctrl enter and let's read that over here uninitialized but used error so when you use a variable it has to be initialized with a value which makes sense right you can't use something which isn't initialized yet now y over here is uninitialized but also unused this will only output a warning now over here you can see the warning actually but if I take this code and put it over here as you can see it gives us a warning unused variable y okay to fix that we can prepend it with an underscore like that all right use mute to Mark a variable as mutable so in Rust a variable in its nature is immutable and you have to explicitly state that you want a variable to be mutable so fill the blanks in the code to make it compile as you can see we are here using a variable X so we want over here to declare X and initialize it with a value of 1. now over here we are mutating X which means this variable X over here should be mutable and we do that using the mute keyword like that now this over here is shorthand Syntax for this all right so we take the value of one we add 2 to it and then we assign it back to X meaning X will then hold 3. right now just a quick note I will always try to annotate the type in this course most of the time it's not necessary so when you see me annotating types it's actually not because this is necessary but just because I want to make stuff as clear as possible so in this case this would hold an integer type and again we will cover that in the next topic let's see as you can see this is compiling X is equal to 3. scope a scope is the range within the program for which the item is valid fix the error below with least amount of modification so over here we are initializing the variable x with a value of 10. an integer type all right now now here we have another scope we also can call it the inner scope and this would be the outer scope right so in this scope over here we are declaring another variable y now we over here try to print out X and Y and we can do that by placing inside the print Ln macro these placeholders over here and then providing as additional arguments over here the variables we want to put the values inside here so for example if we provide X over here then this will get replaced by 10 because X is holding a value of 10. and as you can see in the outer scope we are doing exactly the same now this program won't compile and the reason is because a variable is only valid inside the scope in which it was declared so for example the variable Y is declared inside the scope meaning T variable Y is only valid until this point all right that means we can't use y here so what would be the solution solution would be very simple we just initialize the variable y in the main scope in the outer scope over here meaning X and Y both of them are valid until this point over here right because this over here is the main scope let's see as you can see this is compiling fix the error with the use of Define X now we can have other functions besides main so over here we have the function define X and as you can see we are initializing a variable X which holds a string now this would be T-Type annotation for a string right don't worry too much about the annotations now because we will cover that but just to let you know now the problem here is that in main we are trying to access X now in this scope over here there is no variable with the name of X which means that this program won't compile because the compiler will tell you I can't find over here a variable with the name of X so what we can do over here is actually taking this line and putting it inside the function over here because as you can see in this scope of the function a variable X has been declared and initialized but if I run this program now nothing will happen and that's because as I've said the starting point of every Ras program is in the main function now when we run the code nothing will get executed and that's because we have to actually call this function for it to do something so in this case we can call a function like that just providing the name of the function and these parentheses and as you can see the output will be hello world right because over here we are providing x to the print line macro and we are appending to it comma world like that shadowing you can declare a new variable with the same name as a previous variable here we can say the first one is shadowed by the second one only modify a sort EQ to make print Line work print 42 in terminal as you can see over here we are initializing the variable X which holds an i32 type an integer type now in this scope over here we are initializing another variable with the same name X but over here we are initializing it with a value of 12 instead of 5. and that means when we assert the value of x over here it should actually be 12 right now what would be the value of x in this outer scope if you set 5 then you're right because this variable over here has been declared in the main scope in this scope over here that means X is holding a value of 5. now in this scope over here x holds a value of 12. and these are basically two different worlds right so the variable X declared in the main scope doesn't actually care about the variable X declared in this scope over here they are separate now over here we are shadowing X in the main scope meaning we re-declare and re-initialize the variable X again with an integer type now when we output X it should actually print out 42. as you can see this will be the output number six remove a line in the code to make it compile now over here we have a mutable variable X which holds one an integer type then we are assigning 7 to X now X holds seven right then we are shadowing and rebinding so we are re-declaring X and re-initializing it and we initialize it with a value of x meaning with 7 right and then over here we are mutating X and that means we are here incrementing x by 3. again using this front end notation which would equate to that and this would actually fail and I want you to look at it and think about it what over here might cause an error so over here as you can see we are re-declaring X and over here we are mutating X now as I've said every variable in Rust is in its nature immutable meaning if we redeclare X like that X will be immutable even if it was mutable over here when we Shadow it then it will be immutable again so what we can do over here is just using the mute keyword again so X stays mutable now you might ask okay why even shadowing we can just assign a new value to X why is this even a thing and you can see that here very good so y over here holds an integer type right integer 4. now when shadowing we can assign to the variable a a value of another type in this case for example a string right so y will then hold a string type right let's run that and as you can see this compiles now this line over here is completely useless I hope you notice that because we are taking X and assign it again to X which basically is the exact same thing as we had over here so completely useless of course they are doing that to learn the concept but in real life you don't need this line all right you can just do it like that and as you can see this also compiles unused variables fix the warning below with only one solution two distinct Solutions so as you can see of course we want two stores so we will do with two solutions now the problem here is that this program won't actually output warnings right so I will again copy that and I will put it and I will put it inside the rust playground like that so if we run this program now as you can see we get a warning unused variable X now I have showed you that before the easiest method would be to prevent the variable name with an underscore as you can see we don't get the warning over here the second way would be to do it like that this is telling the compiler that it should allow unused variables so if you run it again as you can see no warning so we have solved that destructuring we can use pattern with lead to destructure a tuple to separate variables now tuples we will cover that in a later topic but for now just think of tuples as a data structure that can hold um various values even of different types so fix the arrow below with least amount of modification so as you can see over here we have a tuple right holding two integers one and two and this over here is called destructuring so we are using the let keyword and we use this syntax over here to this structure these values into variables X and Y so X will then hold a value of 1 and y will hold a value of 2. now over here we are mutating X now again this variable here would be immutable because in Rust every variable in its nature is immutable and we have to explicitly state that we want to have it mutable so what we can do over here like we did before we use the mute keyword like that and then this should actually compile so X holds one here and then we increment X by two so extrude in the nthole 3 and Y should stay the same as it was and as you can see this is compiling the structuring assignments introduced in Rust 1.59 you can now use Tuple slice and struct patterns as the left hand side of an assignment so this is very useful actually so we can declare two variables at once so here we declare X and Y and this would be the same like writing let X and let y right so you're writing one line of code instead of two lines here now again we declare here the variables and we can then destructure these data structures over here so we have here a tuple and we are destructuring it and notice over here we don't need to use the let keyword again because we have already declared these variables over here right so we assign 3 to X and this value over here we don't care about so we can use these double dots over here now this over here is an array and again we will cover that don't worry and this array holds two values now we don't care about the first one but we want to destructure the second one into a variable called y now fill the blank to make the code work in this case we have here an array right remember assert EQ takes two arguments and both of them have to be the same equate to the same output right so what would be the value of x it would be 3. because we have this structured here 3 into the variable X and the same goes for here we have this structured 2 into a variable Y which means y will then hold two all right and we are done with the first topic easy start and see you next time now let's look at numbers in Rust and we'll start with integer types now an integer is basically a whole number meaning it doesn't have a fractional part and integers can come in two different forms they can either be signed or unsigned now a signed integer can represent both positive and negative numbers and I remember that because signed might stand for the minus sign that could be prepended to it and unsigned integers are always positive integers meaning they don't have a minus sign hence unsigned now integers can come in different lengths so there can be 8-bit integers 16-bit 32 up until 128 bits and each length over here has two distinct types one for signed and one for unsigned so for example if we have a variable which holds an unsigned 8-bit integer we would type annotate that with the type u8 or for example we have a signed 64-bit integral we would annotate it as i64. now the last element in the table over here is Arc and Arc stands for architecture now that means that the size of these two types eye size and U size are dependent on the computer's architecture now I guess most of you guys today run computers with 64-bit architecture meaning Isis new size have a size of 64 bits or 8 bytes All Right Now the default types in Rust are for integers i32 and for floats f64 so if we don't annotate anything the compiler will infer it to these types now my head is in the way now to make sense of all of this you will have to understand the binary number system now if you're familiar with binary just skip ahead because you don't learn anything new here but I just want to ensure that we are all on the same page so if we consider this decimal number over here 42 and let's look at each distinct digit so we would have 4 and we would have two right now 2 would be in the ones place and four would be in the tenths place and if we would have more digits here here would be the hundreds place thousands place and so on now as you can see we are here using a base of 10. why is that because in the decimal system we have 10 distinct digits to represent numbers right 0 to 9. and as you can see we are here doing an exponentiation with an exponent of zero because this is actually a mathematical rule which states that anything with an exponent of 0 will always evaluate to one so it doesn't matter which base you use if you have an exponent of 0 the result will always be one right 10 to the power of 0 would be 1 4 to the power of 0 would be one and so on and over here we have 10 to the power of 1 which would be 10 right and then what we do is just we are multiplying the digits of our number with these exponentiations right so we would for example here 4 times 10 to the power of 1 plus 2 times 10 to the power of 0. now again 10 to the power of 1 would be 10 and 10 to the power of 0 would be one and then we would multiply these numbers together and then add this and we would have 42 right something which is very natural to you and you don't even think about it right because decimal numbers are everywhere now in computers things actually look a little bit different let's for example again consider the decimal number 42 now this would be the binary representation of this number zero zero one zero one zero one zero and as you can see we have here eight digits now a digit is called bit right and a bit is the smallest possible unit of information a computer can hold okay now we have eight bits over here and eight bits make up one byte and we over here have actually the same logic as we did over here but with a base of two because again we can only represent two digits in a computer and that's because a transistor can only be in one of two states either it's conducting electric current or it's non-conducting zero or one right so the logic stays the same here we have 2 to the power of 0 which would be one because of the rule we have talked about 2 to the power of 1 would be two two to the power of 2 would be four two to the power of 3 would be eight and so on and as you notice the number here doubles one two four eight sixteen and so on now this over here is actually the fascinating part about computers to me personally because it's just crazy if you think that everything represented this video over here and graphical models and 3D games and everything is stored and represented with only two digits mind-blowing right so we actually here do the same thing we are just multiplying the bits over here with the exponentiations now we won't consider the zero bits because anything multiplied by zero will always be zero right so we are just caring about the one bits over here so we are multiplying one times two to the power of one which would be 2 right because 1 times 2 would be two then one times two to the power of 3 which would equate to 8 and 1 times 2 to the power of 5 which would be 32 as you can see over here and then we are basically just adding these numbers together and we have 42 and this is the way your computer stores all of your data all right now you might ask okay how far can we go with this what would be the largest possible number we can represent and let's for that see the range of 8-bit integers so the smallest possible unsigned 8-bit integer would be zero right I hope this makes sense because if we have a zero in every position over here the result will just be zero right now the largest possible unsigned 8-bit integer would be 255. and that's the case when every single position over here holds A1 right and again we are then just multiplying D1 by the exponentiations over here and add it together and you might now think okay 255 isn't a lot what if I want to represent bigger numbers and there is a simple solution to that you're just adding more bits over here right because the more bits we have the larger number we can represent so let's see for example a 16 bit integer again the smallest possible 16-bit integer unsigned would be zero and this is if every position over here holds zero now the largest possible 16-bit integer unsigned would be 65 535 okay and again this would just be if we had in every single position a one right again just adding the results of the exponentiations over here all together and we would have this result now as you can see the more bits we have the more we can represent now what about signed integers again sine integers can represent negative numbers so signed integers use a concept called tools complement where the processor will take a number over here for example 42 and this would be the binary representation of this number then it will invert this binary number meaning a 0 becomes a 1 and 1 becomes zero right just inverting and then after inverting the number we add one single bit okay so we add one here this would then equate to zero and we have a carry of one and then we add here one and we would have one and the rest says the same now this would then be the binary representation of minus 42. or negative 42. now the most significant bit over here is actually the sine bit if it is zero then it means the number is positive if it is 1 then the number is negative all right and that's why there are distinct types for signed and unsigned and the ranges for signed and unsigned integers vary right and over here we have an illustration just to see the ranges for example for I8 a signed integral of 8 Bits the range would be from -128 to 127. so the smallest possible number we can represent with a signed 8-bit integer would be -128 and the largest one would be 127. and as you can see again the more bits we have the larger and smaller the integer can go right and again unsigned integers never go below zero right then we have view size and eye size so these are as I've said architecture dependent so on a 32-bit architecture computer the size of these view size and eye size type would be 32 bits on a 64-bit architecture it would be 64 bits and this is also called pointer sized integer type because it matches the size of a word in a given platform so what is a word you have to understand that the processor does not read one byte at a time from memory it reads one word at a time so in a 32-bit processor it can access four bytes or 32 bits at a time and in a 64-bit processor it can access 8 bytes or 64 bits at a time now if we look at this very simplified representation of computer memory where we have over here addresses and over here the data and each location over here holds data of one byte all right now in a 32-bit architecture the size of a word is four bytes right 32 bits which means that the processor can access 4 bytes at a time so the processor will read this section over here as one word right it's basically unit of data and what you have to take from this is this statement over here you size gives you the guarantee to be always big enough to hold any pointer or any offset in a data structure so the U size type can hold memory addresses which can point to any location in computer memory that's very important so let's see floating point there are two types F32 which is of size 32 bits and f64 which is of type 64 bits now the representation of these floats are according to the IEEE 754 specification so let's look at some exercises tips if we don't explicitly assign a type to a variable then the compiler will infer one for us now remove something to make it work as you can see over here we have a variable X which holds an i32 integer right now over here we have immutable variable Y which holds an unsigned 32-bit integer right so over here we are trying to assign X to Y now this would actually fail because we can't assign a variable of a type to a variable of another type it's not possible so what we can do over here to fix that is actually just omitting the type and this will then implicitly hold an i32 type right because this is the default integer type in Rust now over here we are initializing in another variable set and what would be the type of Z right it would be I 32. all right thank you fill the blank so over here as you can see we have a value 38 which is of type u8 so we can also annotate a type directly on a value meaning this value over here 38 is of type u8 now the problem here is that the variable over here V expects a type of u16 so we can't initialize a variable which expects a certain type with another type but what we can do over here is using the as keyword which basically can convert an integer type to another integer type so we can use here s and u16 this will then compile tips if we don't explicitly assign a type to a variable then the compiler will infer one for us now we know that already modify a sort EQ to make it work so over here we are initializing the variable x with a value of 5. what would be the type annotation it would be i32 right it's the default integer typing rust now in this assert EQ macro we have here a string u32 we don't worry yet about this method I will explain that in a later episode and on the other side of the assert EQ macro we have a function call so we call type of with an argument of x actually a reference to X but again don't worry we'll cover that so when we call this function over here it basically just gives us back the name of the type so if we pass X over here we would get back i32 right because this variable over here holds a value of type i32 now this boot then actually fail right because we want here that type of X returns u32 so what we can do is just changing this type annotation over here all right fill the blanks to make it work now over here we have a data type for example I8 and the constant Max now Max just Returns the largest possible number that can be represented with the mentioned data type so for example I8 Max would be 127. right this would be an assigned 8-bit integer and we have seen the ranges now the max for an unsigned 8-bit integral would be 255. all right fix arrows and panics to make it work now over here we have a value 251 which is of type u8 and we add 8 to it now in this case the compiler will infer the types so this would be 8 and the variable itself will hold them u8 now can you spot the problem over here the problem is we have seen that u8 can only represent up to 255 right so over here we would have 259 so this is not possible so what we can do over here is just changing that to the next bigger data type for example u16 now then we have here a data type I8 assigned 8-bit integer and we call the checked add method now this is actually the same as doing it like that but in a safer way because we have to handle the error and unwrap over here we will cover that so this is actually a little bit safer but it still will panic because again the I8 cannot represent 259 it would be an overflow so again we can over here just say we want an i16 because an i16 can without problems represent 259. so V1 will then hold u16 and V2 will hold i16 . again I'm just doing this for clarity you don't have to annotate the types alright modify assert to make it work now as you can see over here we have numbers in different representations or basically in different number systems so this is decimal this is hexadecimal this is octal and this is binary okay now the decimal over here has an underscore and this is just a delimiter so this is just for readability it doesn't affect the actual value now let me convert that over here we would have 2024 this is already in decimal system then over here we would have 255 then over here we would have 63 and then over here I hope you remember this would be 255. now if we calculate this as you can see the result would be this value over here like that and this basically proves that in Rusty you can perform mathematical operations on different number systems right now floating Point fill the blank to make it work as you can see over here we have a floating point value so what would be the type I will annotate it directly so it would be f64 this is the default floating Point type right now over here we have an F 32 and over here we have an F 64. right like that again type annotations are not necessary now if we pass x to the type of function over here what would be the return value it would be f64 right make it work in two distinct ways so if we execute this program over here as you can see this would panic now the problem here is a floating Point precision the result won't be exactly 0.3 it would be something like that maybe right or something like that I don't know exactly but t floating point value is too precise because the default floating Point type is f64 so this is really precise and that's why you have these tiny fractions over here now we can solve this using an F 32 Which is less precise right like that and this succeeds now two distinct ways we can also use the as keyword like that so let me first move my head over here again so range two goals first modify Sr to make it work second make print line output 97 to 122. now over here as you can see we have immutable variable sum what would be the type annotation it would be i32 okay now over here we have a for Loop meaning we are iterating over some range and then we are executing an instruction inside this code block over here multiple times basically the times we are iterating over this range over here now in each iteration the variable I will hold a different value right so in the first iteration it would be -3 in the second iteration it would be -2 in the third minus 1 and so on until we reach 1. y1 because 2 over here is excluded and that's always the case when using this syntax the end point of the range is always excluded so we are iterating from -3 to 1 okay now let's actually do these iterations manually so we start with zero right now in the first iteration I would be -3 so 0 plus minus 3 will be minus 3 right so sum will then hold -3 now in the second iteration I will be minus two so minus three plus minus 2 will be minus five third iteration minus one minus five plus minus one will be minus six then in the next iteration I will be zero so it doesn't have any impact and in the last iteration I will be one so minus six plus one will be minus five so the value of sum after this for Loop will be minus five now over here we again have a for Loop and this time we are iterating over a range of characters namely from A to Z now this time Z over here is included because we are using the equal sign over here which means we want a range from A to Z where the end point set is included and C over here will then hold in each iteration a character so in the first one it would be a then it would be B then C and so on and we just print out the characters let's see how that looks like as you can see just printing out each character from a to z now we want to have an output of 97 to 122. so let's look at the ASCII table as you can see in memory a character would be stored as a numerical value now of course in memory everything is stored as binary numbers but it would be stored as the binary representation of these decimal numbers right here so we want to go from 97 a to 122 Z right so it's actually pretty simple instead of outputting DC as character we want to Output it as an u8 for example right and then as you can see we have the ASCII code for the characters from A to Z lowercase right then over here exercise 10 fill the blanks so we are here importing from the standard Library the Ops module range and range inclusive now we have seen the shorthand syntax and this would actually be the more verbose way to write that but I think no one actually does it this way over here we have a range which starts at one and ends at five now again five would be excluded right like that now range inclusive means we range from 1 to 5 where 5 is included and we would write that short and syntax like this one two five and five is included right let's see the last exercise in this topic computations fill the blanks and fix the errors so as you can see over here we have integer additions so one plus two will be three and the compiler over here will infer these types so because we have over here an u32 an unsigned 32-bit integer it will infer these values over here to be of the same type all right now over here the same thing we have 1 minus 2 would be -1 and the compiler infers these types then over here we have 1 minus 2 which is equal minus one the problem over here is that we are dealing with an unsigned 8-bit integer now remember unsigned integers can never be negative so what we can do to fix that is just converting it to an I8 assigned 8-bit integer and signed integers can represent both positive and negative numbers now over here we would have 150 and if there are no type annotations here the default integer type will be inferred which will be i32 now over here again we have the problem of floating Point Precision so we can cast that to an F 32. like that over here we have the modulus operator and the modules operator basically gives back the remainder of a division so if we divide 24 by 5 we would have a remainder of 4. now as you can see here we have Boolean logic and bitwise operations and I want to show you some slides so we get the concept let's start with Boolean logic Boolean logic deals with values that are either true or false and there are three basic operations and or and not and over here you can see the truth tables of these operations so let's see the and operator now the end operator takes two inputs and produces one output now the only time it outputs true is when both of the inputs are true otherwise it will always return false in an or operation if either one of the inputs is true or both of the inputs are true then it will output true if both of the inputs are false then it will output faults and the not operator will just basically invert the Boolean value so over here we have not false and this would evaluate to true and not true will evaluate to false now let's see bitwise operations bitwise operations are operations that manipulate individual bits that make up a binary number treating each bit of a binary number as a separate unit and perform logical operations on them so it's very important that you get that in bitwise operations we are treating each bit as a separate unit and we are doing some manipulation on each individual bit and not the binary number as a whole and we'll cover here and or X or operations and bitwise shifting now there are a lot more but I will just cover the ones in the exercise let's start with the end operator this Ampersand symbol over here and returns one only when both of its inputs are one so similar to Boolean logic as you can see the end operator takes two inputs and produces one output now as you can see in the truth table if both of the inputs are one then the output will be 1. in any other case the output will be zero let's see the or operator this symbol over here all returns one if at least one of its inputs is one if both inputs are zero the output will also be zero so same thing as in Boolean logic if either one of the inputs or both of the inputs are one then it will output one so the only case it outputs 0 is when both of the inputs are zero and let's see xor xor or exclusive or returns one if the inputs are different and zero if the inputs are the same so it's basically similar to the or operator with the difference that if both of the inputs are one then it will output 0. so the inputs must be different so it will output one all right if they are the same it will output 0. and this is basically a building block of your computer so your computer is made up of logic gates and these logic gates perform these operations all the time millions of times in a second then let's see bitwise left shift so we have over here the decimal number one this would be the binary representation now we want to left shift by five positions so we take this bit over here and move it five positions to the left so the so this bit over here will be at this position after the DP twice left shift operation all right and that means we then have a value of 32 because this is the binary representation of decimal 32. and bitwise right shift is basically the same now over here we have a hexadecimal value and we want to right shift it to positions now this value over here would be 128 in decimal and this would be the binary representation of 128. now we want to right shift it to positions meaning we take this bit over here and shift it two positions to the right meaning the bit will then be in this place and again we would have 32 because this over here is the binary representation of that number all right let's see so true and false would be false right because in an and operation both of the inputs must be true in order for it to Output true now true or false would be true because in an or operation only one of the inputs have to be true and not true would be equal to that's right false fight like that now let me run the program so we can see the result of these bitwise operations so as you can see over here zero zero one one and zero one zero one is zero zero zero one why is that because again we are here considering each individual bit as a unit and Performing manipulation on each a distinct bit now over here as you can see we are taking the rightmost bit over here and we are performing The End operation with the rightmost bit over here meaning one and one would be one right because and if both of the inputs are 1 then it will output one in any other case as you can see one and zero would be zero 0 and 1 would be zero zero and zero of course would be zero now let's see this or operation we have one or one this will output one we have one or zero which will output one again right because in or operation if either one of its input or both of them are one then it will output one zero or one is one and zero or zero of course is zero and then we have xor one X or one put B zero because remember X or the inputs must be different in order for it to Output one like over here for example 1 X or zero would be one right the inputs over here are different one and zero same over here 0 x or one would be one and zero X or zero of course will be zero and as you can see over here these are the bitwise shifting so I've covered that in the slides all right see you in the next topic so we will cover in our Char pool and unit let's start with characters so over here make it work now size of Val will just return this size in bytes of a specific value so over here we are initializing the variable C1 with a character so C1 holds a type of char then we pass to the size of valve function the variable C1 and this will return the size of this character in memory in bytes right and same thing over here we have C2 also holding HR and we call the size of L function with this variable so let me first print out the actual result of this function call so if I come over here and I will do a print line statement and just basically outputting the result of this function column so let's see as you can see the output would be 4 meaning this character over here will take up four bytes in memory so the size of C1 is 4 bytes now in Rust the chart type is big enough to hold every single Unicode symbol meaning it is of size 4 bytes so it's able to hold any Unicode scalar value all right and the same thing over here the output would be four meaning if we call this function we will get back four all right make it work again initializing a variable with a Char then we are calling the printshar function with an argument of this variable as you can see this function over here this is the function signature this function takes as argument C which is of type Char and then we will and then we just print out C now the problem over here is that this isn't actually a Char this would be considered a string because in Rust there is a difference between double quotes and single quotes So double quotes are four strings and these single quotes are for characters foreign out the character let's see booleans make print Line work as you can see this variable underscore F holds a Boolean value of false and over here T also holds a Boolean value of true now in an if block this conditional over here will be checked if the result evaluates to true then this instruction inside the curly braces will be printed out if it evaluates to false then the program will just ignore this instruction over here now in this case we want to print that out but over here the result would evaluate to false because we are saying not true right because T holds a Boolean value of true so not true would be false that means this will never get printed out what we can do over here is just removing this not operator and then it will work make it work again we have some variables that hold Boolean types now again this variable over here T will also hold a Boolean type because this over here is an and operation and remember an end operation takes two inputs and produces one output so the output type will be of type pool now true and false would evaluate to false and then we are comparing T and F now F holds true and T holds false which is not what we want so what we can do over here is just making this false over here and then both of them hold a Boolean value of false unit type make it work don't modify implicitly red unit so the unit type is a type which doesn't hold any value its size is zero bytes and it's usually if a function doesn't return any value then a unit type will be returned and this happens implicitly so you don't have to annotate that let's see an example over here the underscore V variable holds a unit type as you can see a unit type is basically represented as an empty Tuple and this would be the type annotation right so over here we are holding a tuple with two i-32 values then as you can see we have here an assert EQ macro we are comparing V with the output of this function call let's see this function over here implicitly red unit so all this function does is it will print out something and then goes back to the color now this function over here doesn't return anything and if a function doesn't return anything the compiler will implicitly return a unit type right but you don't have to annotate that all right so the return value of this would be unit type now V over here holds a tuple meaning this would fail we have here to provide underscore V which holds a unit type so this holds a unit type and this function call over here will return a unit type all right what's the size of the unit type modify 4 in assert to make it work as you can see we are again having this size of L function and we are providing it this variable which holds a unit type now the output would be zero because unit types are of size 0. so to quickly recap HR is a single character of size 4 bytes a Boolean value of true or false is of size 1 byte and the unit is an empty Tuple of size 0 bytes used to return nothing in expressions or functions so this time I will first show you the slide and then we will solve exercises so a statement is an instruction that performs some action but does not produce a value function definitions for example are statements as well as code that ends with a semicolon usually and an expression will evaluate to a resultant value so let's see all right let's see this example over here we have X which holds an u32 all right now over here we are initializing the variable y with the resultant value of this whole expression so as you can see we can inside these curly brackets we can declare variables over here and then we do some operations and return the value as you notice I omit over here the semicolon that means that this result of this operation will get returned which means when this evaluates it will then be assigned to Y so y will then hold the result of this operation now all of the code inside these curly braces are considered as an expression because it produces a resultant value right it would be this value over here now this variable assignment over here would be considered a statement because first of all it ends in a semicolon and second it doesn't produce a new value right it just assigns a value to a variable now y in the end would hold an u32 right because we are here performing an operation with values of u32 types the semicolon suppresses this expression and unit type is assigned to Z so Z over here would hold a unit type why is that because we over here have a semicolon so this code over here doesn't return a value if we omit the semicolon then this would be the return value that then gets assigned to this variable meaning it will then hold a u32 all right let's do exercise one make it work with two ways so as you can see over here we are initializing this variable over here with this expression inside the curly braces now we are here initializing the mutable variable x with a value of one so X will hold I 32. then we are mutating X incrementing it by two so X should then hold 3 right now over here this would actually hold a unit type because over here we have an assignment so we are adding 2 to the value of x and then we assign it to X now the problem is that variable assignments are actually statements so this should end in a semicolon because this would be the same as if I would write it like that right so we can't return over here a variable assignment but what we can do is we just return X because X over here will then hold 3 and then we return X meaning the value of x is then assigned to the variable V so variable V will then hold a type of I 32. let's see this compiling as you can see make it work in two ways now the second way to solve it is just in this assert EQ macro we compare V to a unit type because um no value will get assigned to V and that means the variable we will then hold a unit type like that over here as you can see we are initializing V with a variable assignment so this would not be allowed it's invalid syntax but what we can do is we use curly braces like that and then we initialize here a variable X and we return X this would be allowed so V then holds an i32 let's see over here we have a function call with two values one and two so let's see the function over here it takes two arguments X and Y of type i32 and it returns an i32 value now over here as you can see this function just adds X Plus y now the actual return type over here would be unit type because as you can see we are ending this operation with a semicolon meaning nothing will get returned so if we omit the semicolon over here then we would have a return type of I 32. meaning after the function call over here s will then hold the result of this operation 1 plus 2 would be 3 so s would hold three that would be a type of i32 so let's now look at functions a function is a block of reusable code that performs specific tasks it can take arguments processes those inputs and then returns a result and a diverging function is a function that never returns to the caller and this would happen for example if the diversion function is panicking looping forever or quitting the program so let's see don't modify the following two lines as you can see we have here a tuple and we are destructuring it we have already seen how that works now over here we have a function called now this would be the color and this function would be the collie okay when we call the function the flow control will go to this function and the program will continue executing the instructions provided in this function now it's very important that you know that functions always have to annotate types for their arguments so over here we would Define that X should be of type i32 right because Y is I 32 and we are performing here a mathematical operation meaning they both have to be of the same type and as you can see like in the last exercise this function wouldn't return a value because over here we have a semicolon so if we omit that then this result of this operation will get returned which means the return type would be i32 then when this Returns the flow control will go back over here and assign The Returned value from this function to variable s meaning the variable s will then hold a type of i32 and the value would be 3 right because we are calling the function with arguments one and two or X and Y over here which hold one and two and then this function will just sum it up together and that means s will hold 3. over here we are calling the print function so we go to the function over here and as you can see the function just prints out something to standard output but it doesn't return a value now implicitly the compiler will return a unit type like that but we don't have to write it it's implicit solve it in two ways don't let print Ln work all right so we are here calling a function never return and this function should never return because as you can see the return type over here is defined as exclamation mark and that means this is a diverging function a function that should never return back to the caller now we can do this in various different ways but I will just use the most simple one which is panic and the Panic macro just causes the program to panic meaning the program will immediately exit and return an error as you can see unreachable statement so this line over here is never reached as you can see and that's what we want because over here don't let print Ln work so we never actually reach this point the program will exit before going back to the caller diverging functions diverging functions never return to the caller so they may be used in places where a value of any type is expected so in the main function we just print out something and over here we have a function get option it takes as argument a value of type u8 and returns option i32 now we will cover options in a later episode now over here we are matching TP and matching if you're coming from another programming language is like is which block okay we are matching TP and then if for example TP would be one then this code block over here would get executed if it is anything else then this code block will be executed and we will cover match statements but match is just a much more powerful way of pattern matching than for example if else conditionals so after this match we are calling this never return FN and this should never return so there are some macros we can use here I've showed you one the Panic macro like that or we can also use unimplemented and you use the unimplemented macro if you have a function that is not implemented yet and we can also use it to do macro like that and to do is basically very similar to and implemented and all of them will cause this function to not return to the caller in this case this would be the color over here fill in the blank so as you can see we have here a variable B which then gets matched over here so if B holds a value of true then 1 will be assigned to variable V if B holds a value of false then it will print out success and the program will then panic now we can here assign false because I guess we wanted to print out success right and yeah let's see as you can see this would be the compiler message if a program panics trade main panicked at we have no value for false but we can panic that would be this output over here so we will now cover ownership and ownership is a concept which is unique to rust meaning even experienced programmers can find it difficult to understand this concept but it's very logical and it all makes sense and I will use visuals and illustrations to make it easier to understand the concept so ownership is basically a set of rules that govern memory management these rules are enforced at compiled time if any of the rules are violated then the program won't compile now let's see the three rules of ownership in Rust first each value in Rust has an owner second there can only be one owner at a time third when the owner goes out of scope the value will be dropped now the owner of a value is the variable or data structure that holds it and is responsible for allocating and freeing the memory used to store that data foreign let's look at scope a scope is arranged within a program for which an item is valid we have two types of scope one is global scope and that means a variable that is declared in global scope is accessible throughout the entire program and local scope if a variable is declared locally it means it is accessible only within that particular function or code block which in turn means it's not accessible outside of that function or code block let's see an example of scope as you can see we are here initializing the variable s with a string that means s over here is the owner of this data of this string value okay now s is valid from this point forward so after initializing s it will be valid throughout this scope over here which is defined by these curly braces so inside of this scope we can do stuff with s when this scope is over s is no longer valid so as over here the owner of this data will deallocate the data that is stored in computer memory and that means in turn we can't use as after this point it gets dropped or removed from memory so when s comes into scope it is valid it remains valid until it goes out of scope and a general rule is the scope ends where the block of code ends so to understand ownership you have to understand computer memory now memory is a component in a computer to store data and instructions for the processor to execute there is a type of memory called random access memory or RAM and it is volatile meaning when you're turning off your computer all data stored inside Ram is lost and there are two types of regions in Ram which is used by your rust program it is stack memory and Heap memory so now let's look at stack memory and I want you to imagine a stack of plates now if you want to eat something you take from the top of this stack a plate put food on it eat and then you will clean up the plate right when you're done cleaning you put the plate back on the top of the stack and that's exactly what is happening in stack memory so this concept is called last in first out meaning the last thing that is pushed on top of the stack will be the first thing that will get popped from this deck meaning the first thing that will get executed now all data stored on this deck must have a known fixed size like integers floats Charles bulls and so on basically all the types we have seen so far are all of fixed known size at compile time now pushing to the stack is faster than allocating on the Heap because the location for new data is always at the top of the stack meaning the program can access a data and stack memory at constant time if you are familiar with time complexity and types of unknown size will get allocated to the Heap and a pointer to the value is pushed to this stack because a pointer is fixed size don't worry I will show you illustrations which will make you understand this concept better let's see for example this simple rust program so as you can see we are starting in main because main is the starting point of execution in every rust program so over here we declare some variables X Y and Z meaning these variables are local to the main scope to this code block over here and because they are local they can't be accessed outside of this scope now over here we have a function call and we pass it the value of x and of Y right so as you can see the add numbers function takes two arguments of type i32 then over here we declare a new variable and assign and we assign the result of the operation a plus b meaning the past value is X Plus Y and we assign it to the variable C and then we just return C right so then Z over here will hold the returned value now when we start execution main alongside with all the local variables will get pushed to the stack then because we have over here a function call inside Main the add numbers function alongside with all the local variables to this function will get pushed to this stack if we would have other function calls over here or inside here then this will also get pushed on the stack and so on and then the program will just execute everything that is on the stack from top to bottom now let's look at Heap memory date of no known fixed size belongs on the Heap allocating data on the Heap will return a pointer an address to a location where the data has been allocated allocating on the Heap is slower than pushing to this stack and accessing data on the Heap is also slower as it has to be accessed using a pointer which points to an address so if we look at this abstract illustration of Heap memory as you can see we are allocating to Heap memory if the size of of the type we are allocating is not known at compile time and the types can then also be dynamically grown or shrinked right the size is not fixed and we will see examples of these types where this cat where this could be useful so when we allocate to Heap memory let's see for example the this packet over here we will get back a memory address and we need this address so we know where this specific data over here has been allocated so we then can access it in our program and an example of a heap allocator type would be the string type now as I've said all types we have covered so far were fixed size meaning their size was known at compiled time and they can't grow and Shrink so the difference is a string is mutable meaning its size can change at run time and a string is stored on the stack with a pointer to the Heap so when we initialize a string type then we will get back a pointer which then points to the actual data in the Heap and and the actual data of the string is stored on the Heap so let's see an example to understand this better as you can see we have here the variable S1 which we initialize with a string type over here and we initialize a string like this string double colon from and then these the actual string now you need to understand that the variable S1 doesn't hold the actual string in this case it holds a pointer which points to the data that was allocated on the Heap so if we look at S1 we can see that S1 doesn't hold the actual data but it holds a pointer and a pointer is basically a variable that holds a memory address that's what we call pointer and we have here Len and this is the length of the string we have initialized and the capacity holds the information of how much space was reserved in Heap memory for this specific type in this case we have initialized a string with a length of 5 so the capacity will be 5 so 5 locations have been allocated in Heap memory so this place is reserved for this string type and we will go into a much greater detail about capacity but for now when we initialize a string with a length of 5 the capacity will be five it will be the same so the size of this S1 variable will be 24 bytes because each field over here is of Type U size right so the pointer is of Type U size the Lan anti-capacity all of them are U size and u-size assuming we are on a 64-bit computer will be 8 bytes so 3 times 8 bytes will be 24 bytes meaning the size of the variable S1 is known at compile time because again the variable S1 will get pushed to this stack memory and everything that lives on the stack memory has to be of known fixed size at compile time in this case all of these fields over here are known fixed size but we are pointing over here to the actual data in Heap memory which can be dynamically sized right it can grow and shrink and we will see examples of that so that is actually the point important the S1 variable doesn't hold the actual data but it holds a pointer to the data in Heap memory okay basically a pointer to the location of the first element and by the way in Heap memory this data over here is allocated as a contiguous block meaning in Heap memory these are all in a contiguous sequence of memory addresses right that's why we only need the memory address of the first location and a length and then the compiler will know where to look at and how long the sequence of data is so copy versus move scalar values with fixed size all types we've covered at the beginning will automatically get copied in this stack copying here is cheap dynamically sized data won't get copied but moved copying would be too expensive let's see an example over here as you can see we have initialized a variable x with a value of 5. then we assign X to y now the type of this value would be i32 right because it's the default integer type now because i32 is fixed size it means that it lives in this stack memory and everything that lives in stack memory will automatically get copied meaning we are here assigning a copy of x to y or basically a copy of the value 5. so we are not assigning the actual value but a copy of that meaning Y and X would point to different memory locations the value is the same but the location at which the value is stored is different because again when we assign a fixed size integer that lives in stack memory rust will automatically copy the value because copying on the stack memory is cheap now over here as you can see we initialize a string and then we assign S1 holding this string 2s2 now as we've seen S1 doesn't actually hold the data over here but it holds a pointer which is returned when we are allocating this data over here this string on the Heap memory so the allocator that allocates this data on the Heap memory will return a pointer and that means if we assign S1 over here to S2 then S2 will only get a copy of the pointer and not the actual data so that is how that might look like as you can see we have S1 which is initialized to hold a string now S1 will then be assigned to S2 and as you can see this is how this might then look like we have here the variable S1 and S2 now the actual data over here didn't get copied the one thing that got copied was the pointer alongside with the metadata meaning these two variables now point to the same location in memory right now this would violate the second rule which states that there can only be one owner at a time so we can't over here have two owners to the same data in Heap memory so what rust will do is it will drop S1 it will delete S1 and that means S2 will then be the owner of the data so the first variable S1 will be dropped and cannot be used after assigning it to S2 to avoid dangling pointers so after assigning S1 to S2 we can't use S1 again and that's different like we saw over here with for example integers which are fixed size we can use after assigning X to Y we can use both of these variables X and Y without any problem because again here the value got copied so the compiler will drop the variable S1 meaning S2 will become the owner of this string and that means we can't use S1 after this point now what if you want to actually have a deep copy meaning you actually want to copy the data that is allocated in Heap memory we can do that but we have to explicitly state that we want to do that because as I've said this might be expensive if you have large data that is allocated on the Heap and if rust would implicitly copy all the data all the time you're assigning variables then this would be really expensive and that's why we have to explicitly call over here the Clone method so when we take S1 which holds a string and we call upon it the Clone method and assign this to S2 then that is what this might look like as you can see S1 again doesn't hold the actual data but a pointer to the data that is that has been allocated in Heap memory and when we now look at S2 S2 also holds a pointer to data that has been allocated in Heap memory the difference is that the actual data in Heap memory that S1 is pointing to has been copied in Heap memory so S1 and S2 point to different locations even if the data is the same right and that's different from what we saw here as you can see S1 and S2 point to the same location in Heap memory I hope you get the concept so let's now see how ownership Works in functions as you can see we have here a variable s which we initialized with a string type so over here s comes into scope and in this case s will be the owner of this data then over here we are calling the takes ownership function providing it an argument of s so s value moves into the function and so is no longer valid here meaning the function takes ownership is now the owner of s so as you can see this function over here takes an argument of string type and it just prints out the provided string so as you can see some string comes into scope and some string over here is the string we have provided as an argument then we print it out and over here some string goes out of scope and drop is called the backing memory is freed meaning when we reach the end of the scope some string will get deallocated in Heap memory so after this point over here s can't be used in this main function over here because again the ownership has been moved from variable s into the takes ownership function now over here we have another initialized variable x with a value of five now five would be of type I 32 the default integer type meaning 5 is a type that lives on this stack it's pushed to the stack and not allocated on the Heap and that means when we call this function makes copy and provided X over here then we provide this function a copy of the value and not the actual value itself so we provide here a copy of the value 5 right and then over here we just print out this number then here are some integrals out of scope nothing special happens so the argument we are providing here goes out of scope but X over here is still valid because again we have just provided a copy because this is a type that is living on the stack memory so let's see this example over here we are calling the gifs ownership function now when we look at this function over here we are inside the function code block initializing a variable with a string value over here and then we are just returning this string and that means that S1 will then hold the returned string namely this yours string over here so S1 will be the owner of The Returned string type over here we are initializing S2 with a string over here that means S2 is the owner of the string then we are calling here the function takes and gives back with an argument of S2 meaning takes and gives back will become the owner of this data and that also means that after calling this function we can't use the variable as 2 because it has been dropped now when we look at the function takes and gives back it takes an argument of type string which we are providing here and then it just again Returns the string right so S3 will then hold the return string basically S3 then became the owner of this data right so we have transferred ownership from S2 to this function takes and gives back and then from this function to S3 and then at the end of the main scope S3 goes out of scope and is dropped S2 was moved we've seen that we have moved it inside the function so nothing happens S1 goes out of scope and is dropped so the variable S1 also gets dropped because it is the owner of of this string type returned from this function so you are now asking why do I even care about all this stuff in other languages I don't have to worry about all of this and this seems rather complicating a simple issue right now the main point of all of this is that ownership prevents memory safety issues so by this concept of ownership you are preventing dangling pointers pointers that point to Nowhere or some garbage values double three which is trying to free memory that has already been freed or memory leaks not free memory that should have been freed and if you're coming from C or C plus plus you're you know exactly what I'm talking about here so before stepping into borrowing we will do some exercises concerning ownership so let's see number one use as many approaches as you can to make it work so as you can see the variable X over here has been initialized with a string then we are assigning X to Y so y also should hold a string the problem over here is that we try to access X over here after it has been assigned to y now we have seen in the example that X will get dropped after we assigning after we are assigning it to another variable right so we can't access X but what we can do is telling the compiler that we want a deep copy of the data that X is holding and we are using for that the Clone method and then we X and then we can access both of these variables because the data has been cloned and X still lives on number two don't modify code in main again S1 has been initialized with a string and then we are calling here take ownership and providing it an argument as two as you can see the function take ownership expects an argument of type string and it just prints out the provided argument now we are then assigning the return value of this function to this variable the problem here is that this function actually doesn't return anything so this would hold a unit type right now if we want to return the argument over here after Printing and using it we can just return it like that and if you return something from a function you have to annotate the return type and that would be string right because we are taking here in the string and then we are returning it back that means S2 will then hold the returned string and then we can print out S2 and notice S1 can't be used after we have called this function with an argument of S1 because the ownership has been transferred to this function so meaning S1 is holding the data over here which means it's the owner then we call this function and provide it as one meaning this function over here becomes the owner and then we are assigning the return value to S2 so that means S2 over here will be the owner of the data in the end number three we are here calling the function GIF ownership and as you can see inside the function we are initializing a variable s which holds a string and we call here the into bytes method on S now let me show you that in the documentation so this is the in two bytes method now as you can see converts a string into a byte Vector so if we have a string over here and we call the into bytes method then we will get the string over here but represented as a vector of bytes okay and the important thing here is that this consumes the string meaning s over here will get consumed and that means s can't be used after this point now what we can do as an alternative we can use S bytes and S bytes just takes references and references will be our next topic so don't worry too much about it right now but when we call the S bytes method over here then the owner will remain the same right it doesn't consume the data so what we can do over here is calling the S bytes method and that means s Remains the owner of this data and we can return it then and that means s over here will be then the owner of The Returned value and then we just print out s fix the arrow without removing code line again we are initializing s with a string then we are calling the print store function with an argument s and all we do is we then print out the string but over here again we try to access S and that would be a compiler error because we are over here providing S as an argument to this function over here meaning this function will become the owner of the data s is holding and that means s is not accessible anymore what we can do over here is we can call the Clone method meaning we are providing this function over here a copy of this data so this data will get copied in Heap memory and that means s will still be valid don't use clone use copy instead so over here we have a tuple now this Tuple holds two integers a unit type and a string now let me annotate that we annotate tuples like that and we have to find each type of array value distribute so we have 2i32 integers One units type and one string now over here we are calling the Clone method and assign it to Y so y should actually hold the same uh values all right and because we have called the Clone method we can use both variables without the problem now it says don't use clones so we can't use clone over here now my solution to this would be if you take a look at the types this Tuple is holding then we see that most of the types over here are actually on the stack memory meaning they are of known fixed size right so inters and i32 would be of size 32 bits right a unit type would be of zero bits the only problem arises here because we have a string now a string is a heap allocated type meaning its size is not known at compile time now what we can do over here is we can use a string literal now we will cover the difference between strings and string literals but for now a string literal is a string that is hard coded into the binary itself into the program so it is immutable which means that this size is known at compile time so all T types the Tuple holes are known are of known fixed size at compile time so over here we can do the same and what's the reason we have been doing this because we know that a type that lives in stack memory meaning it's fixed size will get copied implicitly now this Tuple only holds fixed sized types meaning the Tuple itself is fixed size and that means X over here will get copied implicitly by the compiler all right mutability can be changed when ownership is transferred as you can see we are here initializing s with a string then modify this line only we are here assigning s to S1 now over here as you can see we are modifying the string that S1 is holding and remember when we assign s to S1 S1 will become the owner of this data meaning s will get dropped so we are then trying to mutate S1 and we are pushing to it D string world now remember that a variable is immutable in its nature so we have to use the mute keyword over here to denote that S1 should be mutable and that means that if the ownership has been transferred to another variable we can change its mutability right even if s was immutable we can make S1 mutable without problem because S1 is now the owner of the data now over here as you can see we are initializing a variable x with a boxed integer type now box allows you to allocate directly on the Heap as you know this is an i32 and i32 is normally live in stack memory when we use this over here Box new and we box this i32 it means it gets allocated on the Heap and that means X will then hold a pointer to the data that was allocated in Heap memory so the type annotation here would be box and this box holds an i32 and X then basically holds a pointer now over here implement this line don't change other lines now as you can see we have over here to declare y now y gets the referenced and that means we have over here to get back a pointer so we can do basically the same thing like we did before we can use Box new and let's do one for example so the variable one will hold a box with a type of i32 and as I've said we allocate this integer on Heap memory meaning we get back a pointer and the pointer is a memory address to the location where this data has been allocated now because y holds a memory address we can't assign a value and integer value directly to Y because Y is just holding an address and not an i32 type because remember we can just assign a value to a variable if both of them are of same type now as you can see y holds the type of box i32 which is not the same type that means we have to de-reference and we are dereferencing using this star operator and and this star operator means we go to the location of Y because Y is holding a memory address and we want to access the value right so we want to access this interval that has been allocated on the Heap and that means we can assign a new value 2y but we have to use this store operator and again over here you can see it even better this would be equal right because X is pointing to a location in memory which holds an integral value of 5. now if we would omit the star operator this would not match because X would hold something like that right in memory address now this memory address and the integer 5 would not be equal but this star operator basically says go to this address and give me back the value that is stored at that address which would be 5. and of course we have to make this variable over here mutable right because we are here mutating y partial move within the destructuring of a single variable both by move and by reference pattern binding can be used at the same time doing this will result doing this will result in a partial move of the variable which means that parts of the variable will be moved while other parts stay in such a case the parent variable cannot be used afterwards as a whole however the parts that are only referenced and not moved can still be used so over here we have a lot of stuff we didn't cover yet and I will try my best to explain it but if you don't get it 100 it's no problem okay we will go into greater detail on each of these structures and types so as you can see over here we have a struct person now a struct is basically a custom type we can create our own types in Rust and a struct basically is a template we are saying here as you can see this truck holds two members one name which is of type string and one H which is of type boxed u8 okay a heap allocated unsigned 8-bit integer then we have to instantiate and this person struct meaning we are using this template and create basically a concrete value of that so we are providing here for the name member a string like it's defined here and for the age we provide a boxed value of 20. now this person variable will then hold a type of person right this is now our own custom defined type now name is moved out of person but age is referenced so over here we are destructuring the person instance over here and we do that very similar to tuples we have seen we used to let keyword the name of destruct and then we the structure the fields so the person so the person instance over here will get the structure meaning person name will get put into the name variable and the H member here will get put inside this H variable now notice something we are here using the ref keyword meaning th variable will only take a reference of this data and not the actual data itself but name will get moved inside this variable so the name variable here will be the owner of this string right and that means that person is not the owner of this string anymore it has been when instantiating the struct but it is not anymore then as you can see we are printing out these variables but we then can't use the person instance right because the person instance is not the full owner of all team members inside it and also we can't access person.name this is how you would access normally a member in a struct person Dot and then they member in this case name but we can access person.h because the person instance over here is still the owner of age because again we have destructured it and the variable H only took a reference again if you don't understand a hundred percent it's fine okay we haven't covered a lot of this stuff going on here and I even think this is not a good example right now but yeah so exercise eight as you can see we have here a tuple holding two string types now we are assigning the first element of the Tuple and by the way Cupids are also zero index like arrays and we can access the indexes like this the name of the variable and then Dot and the index we want to access in this case the first one that means that underscore s will hold a type of string right namely this over here and that means the ownership of this string data here got transferred to this variable so underscore s is now the owner of this data meaning we can't access T again because T is not the owner anymore of all the values it contains in the Tuple right so we can't use it again but we could still use T at index 1 because this over here is still owned by T because only the first element has been moved out and over here again a tuple with two strings so fill the blanks as you can see what we want here is destructuring the Tuple and we want to initialize these two variables S1 and S2 and remember when we destructure it we have a syntax like that now over here as you can see when we are destructuring this Tuple both of the values it is holding will get transferred to these variables so S1 will be the owner of this stream and S2 will be the owner of this string meaning T doesn't own any values after this point but what we can do because we want to access T again over here we can call the Clone method as you've seen meaning S1 and S2 only hold copies of these strings and not the actual values and that means that t will still be accessible and T is still the owner of all the values inside the tuber alright see you in the next topic so let's look at borrowing borrowing is a way of temporarily access data without taking ownership of it when borrowing you're taking a reference a pointer to the data and not the data itself its goal is to prevent Dengue pointers and data races data can be borrowed immutably and mutably and there are certain rules when borrowing which we have to comply with otherwise the program won't compile let's look at these rules the first rule states that at any given time you can have either one mutable reference or any number of immutable references but you cannot have both you either have one mutable reference or any number of immutable references and the second is references must always be valid so let's see an example of a reference over here we are initializing S1 with a string then we call the calculate length function and as you can see we are here passing a reference to S1 and this is indicated by this Ampersand symbol over here so then let's see the calculate length function as you can see the argument should be of type reference to a string which we did provide right it's a reference this Ampersand to S1 which is of type string and then all this function does it will call the Len method which will return the length of the string and it returns it now notice that the return type will be of Type U size and then this Len variable over here will hold the length of this string and the type annotation would be use size right the return value of this function and then we just print out S1 and length and notice something over here we can use S1 because S1 over here stays the owner of the data we have just provided here a reference to S1 and not S1 itself so let's see how that looks as you can see we have S1 now again it's very important that you understand that the variable S1 over here doesn't hold the actual data but a pointer to the data which will get allocated to the Heap memory so when we allocate to the Heap we will get back a pointer which S1 is holding alongside with other metadata now because over here we are providing S1 as a reference that means the variable s from this function will then be another pointer which points to the S1 variable so s points to S1 and S1 points to the Heap allocated data and that means that S1 over here Remains the owner of the string let's see Mutual references as you can see we initialize a string and assign it to S now then we call the change function with a mutable reference and notice that you have to annotate that you want the reference to be mutable this is important and it has to be explicitly stated so that the rules are set in place because remember you can only have one single mutable reference at a time and then this change function over here as you can see in the function signature it takes an argument of type mutable reference to string which we are providing right we are providing a multiple reference to S which holds a string and then we can manipulate the string now again very important s Remains the owner of this data even though we are manipulating it in this function over here s Remains the owner because we are here dealing with references so let's look at some examples just to strengthen the knowledge of this concept as you can see we here initialize s with a string and then we have here two mutable references to S and we assign it to R1 and R2 now this would fail this program doesn't compile now this would violate the first rule of borrowing which says that we can only have one mutable reference to the same data at a time but what we can actually do let's see this program as you can see again we have a variable s which holds a string and notice over here we have an inner scope so in this scope R1 will hold a mutable reference to s now because at the end of the scope R1 we go out of scope meaning it will be dropped we can then again pass a mutable reference to R2 so this program will compile so over here again initializing s with a string here we have two immutable references to S now up to this point no problem because the first rule states that we can have any number of immutable references the problem is over here because we are then assigning immutable reference to S to R3 this is a problem because this would violate the first rule of borrowing which says that we can either have any number of immutable references or one single mutable reference but we can't have both of them immutable and mutable references at the same time now over here as you can see we have the exactly same program but as you can see we are here having two immutable references assigned to R1 and R2 and then we just print it out now after this point over here the variables R1 and R2 won't be used in the entire program and that means that we can assign then a mutable reference to R3 it's important to notice here that again we don't use R1 and R2 after this point so it's allowed to have a mutable reference over here while over here as you can see we are trying to access all of them at once this would cause a problem now I've showed you some of the reasons why the compiler is so strict and one of them was dangling references so let's see what a dangling reference is as you can see over here we are calling a function dangle now in tangle we are initializing a variable with a string type and then we are returning over here a reference to this string as you can see in the return type it is a reference to a string now the problem here is that when the function returns this variable over here will hold a reference to this string right and s over here will get out of scope at the end of this function scope here and that means this variable over here will point to something that has already been dropped or deleted so it points to a garbage value right and rust won't allow you to compile a program like that because this will violate the second rule which states that references must always be valid very important and to avoid that we can change this tangle function over here and just returning this string itself right so reference to nothing would then be the owner of the string but returning a reference to something that has been declared inside the function scope is a bad idea all right let's do some exercises as you can see we have here a variable holding an i32 value then over here fill the blank and we have to complete this P assignment over here now let me see over here the memory address of X is and we are providing P so we want to print out here the memory address and by the way this is denotation if we pass here a pointer that it actually outputs the address of the memory location that P is holding so over here what we want to do is we want a reference to X now as I've told you P will then be of type reference to an i32 right because X is holding an i32 type now a reference to an i32 is basically a pointer to a location in memory that points to this data over here so P here holds a memory address we'll see that when we execute this program as you can see the memory address of X is this so this memory address is where the value 5 is stored in in memory and we are accessing that using a pointer over here again a pointer is just a normal variable which holds a memory address in this case the memory address of x right so this is a reference to X over here again we have variable X holding i32 type and a reference to X which is a reference to an i32 value modify this line again we compare here y with five now y doesn't actually hold the value 5 it holds a memory address now to access the data at which this y pointer here will point to we have to dereference and we do that with the reference operator like that that means go to the location of Y and give me the value right so when we go to the location where X is stored for example at this memory address we want to get the value that is stored at that address we are using here the star operator just to access the value so over here we initialize a variable holding a string type then we are calling the function borrow object and as you can see the borrow object function expects an argument of type reference to a string but what we are here providing is a string right we are providing s which is of type string now we want here to pass a reference to s meaning a reference to string in this case we are complying with the function signature over here again variable s holding a string then we call puster function over here now again we have to always take care of the function signature what is expected so as you can see the argument should be of type mutable reference to a string so we have to pass here a mutual reference to s where s is holding a string right number five again here having a string and as you can see over here we are mutating p so that means we want to have immutable reference to s as you can see p will then hold P will then hold immutable reference to a string type right and then we can mutate the string that P is holding and again because we are here having a reference S will remain the owner of the data meaning we still can use S even after this assignment over here ref can be used to take references to a value similar to ampersand as you can see we have here a variable holding HR and this over here would be a variable which holds a reference to a chart right a reference to C now fill the blank don't change order code now instead of here using the Ampersand symbol we can use the ref keyword and it's exactly the same actually they are very similar there are some minor differences in pattern matching um but we will come to that so when we dereference R1 and R2 then this should hold the same data right and over here check the quality of the two address strings so we have here a function get address which gets called with R1 and R2 now all this function over here does is it takes a reference to HR as an argument and it will then put it inside this format macro now this format macro over here is the same as the print line macro with the difference that the print line will print to the standard output while format will return a string right so we take this argument and then we will return here the memory address of this reference now in this case R1 and R2 should hold actually the same memory address right because they are pointing to the same data this case C so R1 is pointing to C and R2 is pointing to C all right so let's see and this is compiling borrowing rules remove something to make it work don't remove a whole line again here having immutable variable s which holds a string then we have two mutable references to s and this actually would failed to compile because again we can only have one mutable reference at a time so to solve this as you can see over here we are just printing out R1 and R2 so this reference over here don't need to be mutable right because it's sufficient that we have a read-only reference to S and again the rule states that we can have as many immutable references as we want so this is compiling and also just to show you when dealing with references over here as you can see s is still valid right error borrow an immutable object as mutable fix the error by modifying this line as you can see we have here a string and we call here borrow object with a mutual reference to S so the problem over here is that we can't pass a mutual reference to something which is immutable so we have to make this variable over here mutable and then we can uh then we can provide here a mutable reference to this variable but the other way around it's no problem okay borrow a mutual object as immutable so this code has no errors as you can see we have here a string and we are passing to the borrow object and immutable reference right now this would actually compile because as you can see the function signature States it should be an immutable reference even though s itself is mutable we can still pass it as an immutable reference this would compile without problems and then over here you can see we are even mutating s because it's mutable comment one line to make it work we have here a string and we have a mutable reference to S right so this would be the type annotation mutable reference to a string type now we are then mutating R1 and over here we again have a mutable reference to s and then we are modifying R2 the problem here is that you know the rule that we can only have one mutable reference at a time but as you can see R1 over here gets used again so this should remain valid but we can't have two valid mutable references at the same time so if I comment this out then this program should compile because we are not using R1 after this point meaning we can have another mutable reference and even if I print out here R2 this should also compile because over here the only valid mutable reference to S would be R2 because we are not using R1 after this point so over here again we have a string and we have two mutable references to S now if we would run that it would actually compile as you can see the program compiles even though we have two mutable references to S but we are not using any one of these in the program but over here it says add one line below to make a compiler error cannot borrow S as mutable more than once at a time you can't use R1 and R2 at the same time so let's use both of them over here to force this compiler error and as you can see this would be the compiler error we are expecting Canon to borrow S as mutable more than once at a time and over here you can see really the Rus compiler is one of the best compilers I've ever seen because it provides you with really useful compiler messages alright see you in the next topic so we have now reached the topic of compound types now a compound type is a type which is made up of other types for example a string is made up of characters so it's considered a compound type now we have in Rust two types of strings we have string with a capital S string like that and we have string slices now if I say string I will always refer to this type otherwise I will say string slice so a string is a heap allocated string type that owns its contents and is mutable we have seen that a string is allocated in Heap memory a string slice is an immutable sequence of utf-8 bytes in memory it does not own the underlying data and is immutable so think of string slice as a view on a sequence of characters stored as utf-8 bytes in memory it's basically just read only so use string slice if you just want to view a string and string slice is more lightweight and efficient than string and U string if you need to own the data and be able to mutate it then we have string literals a string literal is a sequence of characters enclosed in double quotes its fixed size compile time known sequence of utf-8 bytes the type is reference to static string which indicates the data is stored in static storage meaning it is valid throughout the entire lifetime of the program the data is hard-coded into the executable and stored in read-only memory meaning they are immutable so let's now see an example of a string slice and I'm seeing my head is in the way here let's do it like that so let's see over here we are initializing s with a string now again I repeat myself I know but this is very important s doesn't hold the actual data it holds a pointer which points to the data that is that is allocated in Heap memory so s will have a pointer alongside other metadata that will point to the first character of the string and let's now consider this world variable here so we are taking a reference to s meaning s will remain the owner of the Heap allocated string so we are taking a reference to S and providing here an offset from 6 to 11. now I hope you remember in a Range this would be excluded so it would be from 6 to 10. so when we look at the world variable we see that it has a pointer and a length now this length over here is actually false I'm sorry for that this should be the length of the string slice in this case one two three four five so this pointer points to the offset of the string we have provided so it points to index six and it goes to 10 because again 11 is excluded so this world here will hold a string slice of this word right so the string slice World points to a sequence of characters stored on the Heap this is basically just a view into a heap allocated string and that's why it's immutable all right the type of string literal hello world is Ampersand stir for example let s string slice equals hello world now this would be a string literal right because we are hard coding it into the executable and a string literal is also considered a string slice because it's also immutable and it's basically just a reference to the static memory this string gets stored in so store and string slice we can't use Stir type in normal ways but we can use reference to stir fix error without adding new line as you can see this would over here be a string literal we are hard coding this string into the executable into the program itself so again we can't use Stir as a type but we can use a reference to a stir right we can only use Stir by boxing it Ampersand can be used to convert Boxster to string slice now fix the arrow with at least two solutions as you can see over here we have a string literal a string hard coded into the program itself then we call the into method we will cover that later but basically into will convert a type into the type we are annotating here so this string literal will be put into a box meaning it will be Heap allocated then we are calling this greetings function with an argument s and as you can see as this argument over here is has to be of string slice so what we can do over here to convert this box into a string slice is passing a reference to this box as you can see this is compiling but we even can just omit all of this right this photo would be string slice and we then just pass s notice that we don't have to pass a reference to S because s is already the type we need string type is defined in standard library and stored as a vector of bytes back but guaranteed to always be a valid utf-8 sequence string is Heap allocated growable and not null terminated so let's see we want to initialize here s with a string right then we are mutating this string over here and mutate it again now over here we are pushing to it a string literal and over here we are pushing to it a single chart now if you push a single chart you just use the push method not not the push stir method and in the end as should hold hello world now we can initialize here an empty string like that this is now an empty string type now why are we using this type over here and not string slice like so for example why aren't we use why aren't we doing it like that because remember a string slice and string literals are immutable they are just references to some string data in the memory so we have to use string here so we are here initiating an empty string and then we are mutating it and push to its held over and this exclamation mark this compiles let's output the string as you can see the string over here has been mutated and that's the whole point of using this string type over here it can be mutated they can grow and shrink and so on so let's see exercise four fix all arrows without adding new line as you can see over here we have initialized a variable with a string then we are mutating this string over here which means we have to make this variable s here mutable so we are pushing A Single Character this comma and then we are pushing another string like that and we can even push to the string by using this notation here even though I don't like it but it's also possible but over here we have two pass it a string literal because this is defined in this standard Library all right let's see and of course over here sorry we have to use the push stir method as you can see this is compiling so again we are pushing A Single Character then we are using here the push stir method pushing a string literal and we can also mutate strings like that we place can be used to replace substring again we have here a string and we then can replace some elements inside the string so we provide as first argument the element we want to replace in this case dogs and it should be replaced with cats so S1 should then hold I like cats now again replace will mutate the string meaning the S should be mutable foreign methods can be found under string module so number six you can only concat a stream with a string slice and strings ownership can be moved to another variable so we have here S1 holding a string and S2 also holding a string now as you can see we want to concatenate these two strings and we can use the plus operator for that now in the rust documentation it says that the first argument should be of type string while all other arguments must be string slices so we have to convert S2 over here to a string slice now there are two possibilities the first one is using the s store method like that so this over here will then be considered as a string slice so we have converted a string to a string slice right but the output but the result of this concatenation will be of type string it's important to remember that and S3 should then hold this concatenated strings now because S1 over here has been moved inside S3 we can't use S1 anymore because S3 has become the owner of this data so let's instead print out S3 and as you can see this is compiling now there is an easier approach to that we can convert a string to a string slice by just by just providing a reference to a string so a reference to a string can be inferred to a string slice like that string slice and string opposite to the seldom using of stir string slice and string are used everywhere swing slice can be converted to string in two ways so we have covered that fixed arrow with at least two solutions so over here we have a string slice right now we call the greetings function over here with an argument of s now s should actually be of type string so we can convert that by calling this two string method like that so we have converted here a string slice to a string all right the second way would be doing it like that string from and then providing s as you as you can see this is compiling and there are other ways for example two ohms even though I think this is not used that much because it's more General but it's also possible right we can use string form or two string to convert a string slice to a string use two approaches to fix the arrow and without adding a new line as you can see we have here a string literal that has been put into a string so this variable over here will hold a type of string now to convert this s over here into a string slice because this is the type annotation of this variable we can as I did before we can pass as as a reference and again a reference to a string will be inferred to a string slice again we can use as stir for example would be the same string escapes you can use escapes to ride bytes by their hexadecimal values fill the blank below to show I'm writing rust so as you can see we can escape certain values and they will then be convert to ASCII characters for example this would be the hexadecimal representation of the ASCII value of s right so if we want to write rust here then the next character to S would be T so we would write X 74. like that and you can check this out over here there are a lot of examples where we can just Escape using the backslash Now using two backslashes here will print out this literal and these literal characters so the output will be backslash x3f okay because we are basically escaping this backslash all right and over here we have raw string and a raw string is a string in which there are no escapes so everything is printed literally as it is written so the output of this let me run that so the output of this would be backslash x3f it won't be converted to an ASCII character so let's see string index you can't use index to access HR in a string but you can use slice reference to a S1 for example and the offset so let's see we have here a string and now we want to access the first character now in most programming languages this would be possible but in Rust you have to use string slices meaning you take a reference to S1 and then provide the offset if we want to only access the first character we would provide an offset in form of a range from 0 to 1. now again in a Range this would be excluded meaning we only access this first element so the return type will then be a string slice notice something here when we assert age we would have a string of H and not a character even though we are here only accessing a character we are accessing it using these string slice this string slice notation over here so remember a string slice is just a view into a heap allocated string right so we are just looking what is at this location in this case we just want to access the first location over here right modify this line to fix the error tips this would take three bytes in utf-8 format and that's right we have said that a character in Rust is of size 4 bytes now a character in a string is a little bit different so if it is an ASCII a character then it will usually take one byte and DC Unicode characters can take three bytes or I guess some of them even four bytes all right so we want to have in H1 only this character so again we are taking a reference to S1 and providing an offset oh 8 would be at index 0 I would be at index 1 this comma index 2 and then this will be index 3. now this takes up three bytes so we have to provide here an offset from three to six remember six is excluded so it goes from three to five three four five three bytes let me output that as you can see this would be the output and again this is just a view into a heap allocated string right operate on utf-8 string fill the blank to print each character in this string so over here there is a method called chores now chores will put this sequence of characters into an iterator meaning we can then iterate over it using a for Loop for example and that means c will then be in each iteration one of these characters and we will then just find out C as you can see in each iteration we are printing out a single character alright that's it see you in the next topic so we'll Now cover arrays now an array is a fixed size collection of elements of the same data types stored as contiguous Block in stack memory now it's stored in stack memory because its size is known at compiled time the signature of an array is like that you can see over here which indicates that the length is fixed at compile time arrays can neither grow nor shrink they must retain their size so let's see the type of array is like this as you can see erase length is part of their type signature so their length must be known at compiled time for example you can't initialize an array like below this would not be possible as you can see we have an init R function which takes an argument of type i32 and then we try over here to initialize an array with the length that is provided as an argument this would be and compiler error because as we've seen the size of an array must be known at compiled time if we do it like that the size of the array would only be known at runtime this will cause an error because the compiler has no idea of the exact size of the array at compile time let's see fill the blank with proper array type as you can see we have here an array with five elements now what would be the type of these elements it would be the default integer type which is i32 so we annotate arrays like that in square brackets then we would have to type of the elements and the amount of elements inside of the array which is five modify the code below to make it work so we can call the Len method on an array and it will then provide the length of the array so in this case this would be 5 right because there are five elements inside number two we can ignore parts of the array type or even the whole type let the compiler infer it for us so we actually don't have to annotate the type or we can annotate the type but leave for example the type annotation here for the compiler to infer fill the blank arrays or stack allocated size of well returns to bytes which in Array occupies HR takes four bytes in Rust Unicode Char so we have seen that before the size of well we'll just take an argument and it will output the size in bytes the provided argument occupies in memory so if we provide the r over here then we should actually get back 3 times 4 bytes because because a character in Rust takes four bytes meaning this array over here would occupy 12 bytes in memory all elements in an array can be initialized to the same value at once so as you can see we have here an array which should hold 100 elements and each element should hold the number one okay we are here accessing the first element and this is the notation for accessing elements in an array in this case we access the first element at index 0. until length would be 100 so instead of typing out 100 times 1 we have a short 10 Syntax for that so we can here first of all provide the element it should hold in this case one and we want it to hold 100 elements of this value over here number four all elements in an array must be of the same type so as you can see because when we annotate an array we actually have to define the type here in this case we want integer types and we have three of them now as you can see this is a torrent this is not possible all elements inside an array must be of the same type indexing starts at zero so over here we have an array of characters let's annotate that and there are three elements inside it and now we want to access the first element right we want this variable over here to hold the first element now again arrays our zero indexed meaning we access it using this notation here and provided a value of 0. out of bounds indexing causes panic so over here we have an array which holds two strings and we want to access the first element right now we can use this notation over here like we did before or we could use the get method the difference is that get will return an option type we'll cover that in a later episode but it's generally safer than using this notation and this is especially true if you're accessing elements at runtime okay so we can use this notation or we could use this notation now over here this would panic because this array has only two elements meaning indexes 0 and 1. so let's access here index one and this compiles so let's see slices a slice is a reference to a contiguous sequence of elements in a collection it provides a way to borrow part of a collection without taking ownership of the entire collection it can be created from arrays vectors strings and other collections implementing DT ref trade so let's see this example of a slice we have here an array and then we are over here taking a reference to a and providing an offset essentially the same thing we did with string slices right a slice is just a view into a collection and over here we try to access the elements at index 1 to index two right because 3 would be excluded so the variable slice will then hold a slice with the elements 2 and 3 in it all right let's see slices are similar to arrays but their length is not known at compiled time so you can't use slice directly here both this slice over here and store or slice types but directly using it will cause errors you have to use the reference of this slice instead so because these two slices over here their sizes are not known at compile time we have to use a pointer to that right and we have seen that in the example of string slices so we use this syntax over here fix the arrows don't add new lines as you can see we are holding here an array let's annotate the type like that this is an array of three i32 integers now we want here to get a slice with this offset meaning from 0 to 1 0 1. so again like we did with a string slice we want a reference to an array with this specific offset so we are trying to access 0 to 1 because 2 is X because 2 is excluded meaning we are accessing here index 0 and 1. the elements one and two so this slice over here will then hold one and two right now the type annotation here would be like that because we are holding a reference to this array right and this would be the type annotation of a slice now over here we have a string later on now string literals as we've seen always are annotated like that string literal is hard coded into the program's binary and that's why we can access it using a reference to this string literal over here which is in the binary itself all right a slice reference is a two word object for Simplicity Reasons from now on we will use slice instead of slice reference the first word is a pointer to the data and the second word is the length of this slice the word size is the same as U size determined by the processor architecture for example 64 bits on an x8664 slices can be used to borrow a section of an array and have the type signature like this where T over here stands for the type as you can see we have here an array with three characters now we want to have a slice of this array with the first two elements these two elements and this is shorthand notation instead of writing it like that we can just omit the zero all right so we are accessing 0 1 and 2 is excluded so the slice variable should hold a slice with these elements the type annotation over here would be reference to char okay modify 8 to make it work tips slice reference is not an array if it is an array then assert will be packed each of the two characters this and this occupies four bytes 2 times 4 8. now because this slice variable over here doesn't actually hold an array but a slice we are here actually holding a pointer right because this is a reference to this array and that means we have seen over here A Slice reference is a two word object meaning the pointer and the length field will both occupy sizes of Type U size and your size if you're on a 64-bit computer it means you size will be of size 8 bytes or 64 bits and that's why a slice holds 16 bytes right if we would pass the array here we would have if this slice would have been an array then 8 would actually pass because we would have two characters each character is four bytes meaning eight bytes but because we have a slice which holds a pointer and a length both of Type U size we have 16. so over here we have an array of five integers of type i32 fill the blanks to make the code work as you can see we want the slice variable to hold a slice of these elements so let's first annotate the type actually so this slice variable over here should hold a slice of I 32 elements right so we are taking a reference to the array and provided an offset we want to go from this to this meaning index 0 1 we are starting at 1 and we go up to 0 1 2 3 4. and I choose 4 here because 4 is excluded so we will go from 0 1 2 3 to index 3. string slices and we've covered that already but let's do some exercises for repetition now over here we have a string and we take over here a string slice meaning we take only the first two characters like that or let me write like like that right we take the first two characters fill the blank to make the code work don't use 0 to 2 again so over here we have a second variable which should hold a string slice and they should actually hold the same elements as you can see over here we are we are here asserting that slice one and slice two are the same so instead of using this syntax over here as we've seen before we can use like that we can omit t0 over here we have a string literal again a string literal is hardcoded in 2D binary and this would be the type annotation modify this line to make the code work as you can see we want slice over here to hold a string slice with only this character so let's annotate the type this would be a string slice and because this Unicode character over here takes three bytes we have to provide an offset here of three meaning zero one two right three bytes alright last one number six a reference to a string can be implicitly converted into a string slice now over here we initialize a variable s which holds a string here reference to S is reference to string type but first word needs aced but first word needs a string slice as you can see over here it works because reference to string can be implicitly converted to string slice so as you can see over here we are calling the first word function which takes us an argument a type of string slice and this would actually work because we are here providing a reference to S which means this over here is a reference to a string right and a reference to a string can be converted to a string slice implicitly by the compiler so then this function over here will take a reference of s and provides an offset from 0 to 1 where 1 is excluded so we just so we just take the first element and it returns this as a string slice that means that the return value of this function will be assigned to the word variable then over here we are calling the clear method on S which means this string will then be empty clear will just remove every content in the string the string itself will still remain in memory with the same capacity and S also is valid so it's different from drop but it will empty the string essentially so we will then have over here an empty string the problem over here is that clear takes immutable reference to self and that's a problem because over here we have a reference which is immutable to S and we have over here immutable reference to us now the problem arises because we are here using the immutable reference so if we would delete that and put it over here then after this point the word variable over here which holds an immutable reference is not used anymore and so we can use this clear method and this is compiling and as you can see we just get the first element alright see you in the next topic now we will cover tuples we have seen some tuples before and some exercises but now we will go into greater detail so a tuple is a way to store related pieces of information in a single variable and it's essentially a collection of values of different types grouped together as a single compound value again a compound value is a type composed of other types it's stored as a fixed size contiguous block of memory on this stack and the signature is parentheses and the types the Tuple is holding let's see elements in a couple can have different types pupils type signature is like that where T1 T2 are the types of tuples members so as you can see we can hold different types we have here an u8 and an i16 together in a tuple tuples can be tuples members so we can even Nest tuples so this Tuple over here as you can see holds an u8 over here and another Tuple inside it which holds an i16 and a u32 integer all right fill the blanks to make the code work so over here we have to annotate the type now again the first element over here would be u8 the second one is u16 the third one is an I64 the fourth one would be a string literal and the last one is a string members can be extracted from the Tuple using indexing so as you can see we have here a tuple let's annotate the type this is how we would annotate a tuple and it holds three string literals then we are accessing here the second element tuples remember are zero indexed but in this case it would be M but we want it to equal sulfates so we have to access d index 2 right 0 1 2. long tuples cannot be printed so if a tuple exceeds a certain amount of elements it is too long to be printed as you can see this would be too long if I delete that over here so this would work so we can have up to 12 elements in a tuple to make it printable if it exceeds 12 elements you can still use it but it's not printable the structuring Tuple with pattern so let's first annotate the type of this Tuple here we have an i32 the default integer type then we have a float which means f64 the default floating Point type and we have a string literal fill the blank to make the code work so over here we want to destructure this Tuple where X holds one y holds hello and that holds 6.4 so let's see re-distructure tuples like this using the let keyword and the parentheses then the first element should be X right now the second element 6.4 should be put into Z and the last element should be destructured into variable y the structural assignments over here we are declaring some variables and then over here we destructure this Tuple and we want to put it inside these variables so we can so because we have already declared these variables we don't need to declare it once more so we can omit the LED keyword and then all we do is we destructure it so one should be put inside variable y 2 should be put inside variable Z and 3 should be put inside variable X tuples can be used as function arguments and return values fill the blank need a few computations here so so as you can see we are calling this function some multiply and this function over here expects an argument of type Tuple and the Tuple should hold two integer values what we do over here is we perform some operations and return it in a tuple right so so the first operation will be this one adding the first element of the provided Tuple with the second element of the provided Tuple and then we are multiplying the first element of the provided Tuple with this second element of the provided Tuple and we return that in a tuple again so the result of this operation over here will be the first element and the result of this operation will be the second element and as you can see we and as you can see we can return that in a tuple now after returning this Tuple we destructure it so the first element will be put in X and the second element will be the structured to the variable Y where X will hold 5 and Y will hold 6. so we have now to provide here two numbers when we add them together they will equal they will evaluate to five if we multiply the two numbers they will evaluate two six so this is a little riddle for you and try to solve it and the answer would be 2 3 right and don't forget to add here parentheses because we are passing it here a tuple right we are not passing two arguments we are passing one argument which is a tuple that holds two elements very important so if we add 2 and 3 we will get 5 and if we multiply two times three we will get six and this will be the return type of this Tuple over here all right see you in the next topic up until now we have been using types that are defined in this standard library but we can in fact create our own types and we can do that by using a struct now a struct is a compound type allowing to group together values of different types into a named data structure it's similar to tuples but each value has a name so the values can then be accessed through this name and a struct has to be instantiated with data think of it like destruct itself is the template for the instances you create from it now let's see how that works as you can see over here we have our template in this case a struct named user and the user struct holds various fields and we can define a field by giving it a name and a type that this field should hold so in this case the active field holds a type of Boolean username holds a type of string email string and sign in count u64 and then we can instantiate this struct and we do that by calling the name of destruct user in this case and providing for each field a concrete type and of course the types have to match the template then we are assigning this instance we have created over here to a variable so this variable will then hold an instance of the user struct and we can access and even mutate the fields of an instance so over here as you can see we have created an instance and assigned it to the user1 variable and then we can use this syntax over here to access a field so in this case we are even mutating the email field from the instance user 1. as you can see we do that exactly like we would mutate a variable just assigning it a new value now to mutate you have to make it explicitly mutable meaning you use the mute keyword here like you did with variables and functions can also create instances and return it so in this case we have the build user function which creates an instance of the user struct and as you can see again it's just providing concrete values for each of its fields and over here as you can see this function takes two arguments email of type string and username of type string and then it provides it 2D username field and to the email field now in this case we are repeating ourselves as you can see and we can avoid this by just using this shorthand syntax so if the name of a field and the name of a variable or argument matches then we can write it like that and this just makes it less verbose and there is also a concept called struct update syntax so over here we have an instance of the user struct now we want to create another instance but taking some of the values from the fields from user 1. as you can see for example in this instance we want the active field to hold the value of user 1's active field so we are accessing user 1 and the active field and as you can see we are taking everything from this user one instance except the email and as I've said there is a better way to write this and this would be instantiating a struct like we did before but just providing the fields that are different from the instance we we want to take from it so for example over here we say that the email field should hold this value and the remaining values should be taken from this user 1 instance so this will then create a user instance with all the same values from user 1 except the email field then we also have Tuple structs and Tuple structs are like normal structs but using tuple-like Syntax for defining their fields it's basically a named Tuple and it's instantiated by parentheses instead of curly braces and its values are accessed using Point notation and then we have unit-like structs and these are structs without any Fields meaning they don't hold any values and it's mainly used when working with trades and this will be covered soon and that means they don't store any data let's solve some exercises the types of structs exercise one we must specify concrete values for each of the fields in destruct as you can see we have here a person struct holding various fields and then over here we have a variable H which holds a value of 30. now then we are instantiating this person's struct and assign it to the variable P meaning P will then be an instance of the person's struct now as you can see we have here the fields and the concrete values and this would be the shorthand Syntax for this because we have over here a variable matching the name of the field so we can write it like that but as you've noticed we didn't provide the hobby field a concrete value so when you are instantiating a struct you have to provide to each field a concrete value so let's do that and the variable P will then hold a type of person now h of course has to match with the templates so this would be an u8 and of course we use commas here and not say me colons all right unit struct doesn't have any field it can be useful when you need to implement a trade on some type but don't have any data that you want to store in the type itself so as you can see this example is using trades and we have and we have not yet covered that so I will skip that because this might just be confusing and we will cover that soon number three Tuple truck looks similar to tuples it has added meaning this track name provides but has no named fields it's useful when you want to give the whole Tuple a name but don't care about the fields names so as you can see over here we have two Tuple structs one color and one point and they are exactly like tuples but they are named okay so over here we have three integer values in the color Tuple struct and in the points rack the same thing now we want to instantiate the point struct over here and the variable V will then hold an instance of our custom type point then we are calling the check color function and provide an argument of B now as you can see the check color function takes an argument of type color but this is not what we want right because we are here passing a variable of type point so let's change that and over here we are then destructuring the instance that was passed so in this case as you can see X should hold zero so let's provide this value over here then the P1 field and as you can see we are accessing the fields in the Tuple struct like we would access elements in a normal Tuple using this syntax so over here we are accessing the second element and this should hold 127. so that means the last element should hold 255. and as you can see over here we are destructuring so the first element over here gets this structured into a variable X now the second one over here we don't need to destructure right because we are accessing it directly using the PE instance and the last one let's call this Z okay and by the way when destructuring and you don't need a value like for example this one you just provided an underscore let's see and of course when the structuring we have to provide the name of this truck in this case point and this is compiling operating on structs you can make a whole struct mutable when instantiating it but rust doesn't allow us to Mark only certain Fields as mutable fill the blank and fix the arrow without adding removing new line again we have the person struct we have here the age variable let's annotate that and we have the P instance so as you can see we want to mutate the age field of the P instance and this is right but the problem is the P instance is not mutable so let's make that mutable and then we can also mutate the name field like that using field initial shortened syntax to reduce repetitions so again we have the person struct and in main we are just printing out something and over here we have the build person function so as you can see this function takes two arguments name of type string and H of type u8 and these arguments exactly match the types defined in the template so then we can over here instantiate a person struct meaning we can hear for the name field provide this argument and we have seen we can use field image for 10 syntax meaning if the field name matches the name of a variable or an argument we can skip it like that let's see exercise 6 you can create an instance from another instance with struct update syntax so as you can see we have here the user struct and we have seen an example of that in these slides then over here we are creating an instance from the user struct that means the variable U1 will then hold an instance that means that the variable U1 will then be an instance of the user type and again we are just providing each field over here with a concrete value then over here we call the set email function with an argument of the U1 instance so let's see over here as you can see in the function signature the argument must be of type user and then we want to create over here a new instance and what we want to achieve here is that we take everything and what we want to achieve here is that we take every field from the U1 instance except for the email field we want to we want that to be different so what we can do is providing a concrete value for the email field and then just saying the rest we want to take from the argument provided and then we are just returning it so that means U2 will then hold a newly created instance Point distracts we can use derive debug to make a struct printable click fill the blanks to make the code work so you actually can't print out a struct using the normal print line syntax we have to so to be able to print a struct we have to derive a trade again we will cover trades but we have to derive that using an attribute all right that means that destruct rectangle here implements this debug trade and that means we can then use debug notation to print it out so over here as you can see we have a variable scale and then we are creating here a rectangle instance so we are providing a concrete value for the field with and this would be 30 times the scale and we provide a concrete value for height now as you can see print debug info to standard error and assign the value of 30 times scale to width now this debug macro over here will just print out debug information to the standard error output and that means over here rect one will then hold an instance of the type rectangle and the type annotation for the scale variable over here would be u32 right because we are passing this scale variable over here meaning it should be of Type U 32. and then again we are here using the debug macro and printing out racked one and instead of doing that we can also use a normal print line statement like you're used to except that in the placeholder over here we have to use this notation and this is called debug notation meaning any type that implements the debug trade can then be printed and the difference between debug macro and printland macro as you can see print line will point to the standard output while debug will print to the standard error and that's why you don't see it over here you just see the print line output now to see that I will go to the rust playground and we can just run this code and as you can see over here this would be the debug output so this debug macro over here prints out the exact thing we are passing to the width field in this case 60. and this debug macro over here points out the instance right we then have an rectangle with a width of 60 and a height of 50. basically the same thing that we print out with the print line statement here foreign move within the destructuring of a single variable both by move and by reference pattern bindings can be used at the same time doing this will result in a partial move of the variable which means that parts of the variable will be moved while other parts stay in such a case the parent variable cannot be used afterwards as a whole however the parts that are only referenced and not moved can still be used and I guess we have seen that before but let's do another exercise so as you can see right we have seen that alright so let's move to the exercises directly because I've already covered that and I think the exercises are better for understanding so as you can see fix the arrows to make it work over here we have a file struct holding two fields of type string then in main we are creating an instance of the filed struct providing it concrete values for each field meaning F will then hold an instance of the file struct and over here we are then assigning the name field of the F instance to the underscore name variable so what would be the type annotation here it would be string right because F name holds a string and that means the name variable here will become the owner of this string data and that is the reason that we then cannot use f as a whole and also not F dot name because a partial move happened so if a field gets moved out to another owner then you can't access this field and also not the data structure that is holding this field so this would fail over here and this would fail now to fix that we can actually access just the name variable here right because this is now the owner of this string but what we can't do is accessing the whole instance over here this is not possible as you can see this is working now if you actually want to keep f as the owner of all the fields and to print it out like that and by the way notice again when we are outputting a single field we used in normal notation if we output the whole instance then we have to use debug notation because these fields over here hold types that are defined in the standard Library while this is a custom type we have created so to make that work we can also use here the Clone method and as you can see this is working and we are here printing out the instance all right see you in the next topic we can also create custom types using enums now an enum is a way of defining a type with only one of a possible set of values we can only access one variant of an enum at a time and it can hold additional information using tuples and it's especially useful when using in match statements let's see an example of an enum so we have here the enum IP address and this enum holds two variance so in enums we call it variance and not Fields like instructs and each variant over here holds a type using tuple-like syntax all right so an IP address by the way can be in either one of two formats either it's V4 or B6 and now we will see the difference between enums and structs so this is how we would instantiate this enum IP address we are accessing the name of the enum double colon and then the name of the variant in this case V4 then providing it a concrete value I hope you noticed the difference in a struct we would have provided concrete values for each field in an enum we instantiate the enum only with one single variant all right so this home variable will then hold an instance of the IP address enum and this loopback variable over here will also hold an instance of IP address but using the variant V6 and by the way this is the old format of IP addresses ntcc new one so let's do some exercises enums can be created with an explicit discriminator so enums are enumerated right that's why they are called enum now if you don't provide here a value it will be implicitly starting from zero this would be one and this would be two and we can also enumerate them with an explicit discriminator okay so this will then hold zero this variant will be one and this will be two and over here you can see C like enum now you can't use as a discriminator a floating point value this is not allowed but like in C if you for example would do it like that we are here providing the discriminator 5. then 1 and 2 implicitly will be six and seven right or we could say this should be 9 for example right so you can manipulate the enumeration of an enum so let's see actually let me change that back and let me change that to integers an inner variant can be converted to an integer by the as keyword so when we take this enum over here and we create an instance as we've said as we've seen calling the name of the enum and the variant and then we can convert it to an u8 for example right and we can do the same thing here and over here should be the same thing and as you can see this is compiling let me print out as you can see when we access from the number in on the variant one then A1 will get outputted now let's see if I provided a value of 10 over here but for that I have to comment that out as you can see this foot would then be the discriminator for the one variant all right each enum variant can hold its own data so as you can see we have here an enum message now this enum has different variants so this variant quit doesn't hold any additional data but while this variant move here holds Fields it's very struct like and this variant right is tube alike so it holds a value in a tuple syntax and the same thing for change color so we can then instantiate the move variant from the message enum with these values over here X1 and Y 2. and as you can see because this is very struck like you instantiate it basically like a struct and over here instantiating with hello world as you can see this variant over here holds a string type so like that and please notice over here that both of these variables here hold the same type message because this is also a custom type A type we have created we can get the data which an enum variant is holding by pattern matching so over here we have the same enum like we saw before and over here we have an instance of team move variant now we can here use E flat and we will cover that in the next section two get out the data inside this move variant so we are matching here the message variable holding an instance of the move variant and then over here we can basically destructure it so X should then be destructured into a variable a and Y should be restructured into a variable B and then as you can see we can use these two variables now we are here asserting A and B and a stands for this value and b stands for this value and as you notice they are not the same so this would fail actually so let me change that and now let's run the program fill in the blank and fix the errors again same enum and over here we have an array of different variants from this enum type right we have the quit variant the move variant and the change color variant now the neat thing here is that all of these instances are of the same type message right our custom type now I hope you remember how we annotate an array so first thing will be the type and then the number of elements Let's see we have over here three elements right and the type would be I hope you know it message right all of these variants or instances hold the same type and then over here we are looping over this array here meaning message this variable will hold in each iteration one of these instances and then passing it to the show message function and as you can see when we want to take an instance of this inner message we can just use the type message here because again all of them all because all of them have the same type and all we do then is just printing it out and again because this is a custom type we cannot print it out just like that so we have to use debug notation here and derive over here the debug trait and as you can see in each iteration over here we call the show message function and it will print out the contents of this array so let's see exercise five since there is no null in Rust we have to use enum option to deal with the cases when the value is absent and I want to show you some slides for that so the option enum option is an enum that represents a value that may or may not be present it's known in other languages as null and it's referring to the absence of a value it's used to handle cases where a function or method might fail to return a value so this is how the enum option looks like as defined by the standard Library as you can see it's an enum holding two variants none and some now this over here is a generic meaning that sum can hold any type and we'll cover generics very soon but for now again think that this can hold any type provided so let's see an example over here we are here initializing the variable 5 with D variant sum holding an integer value right and then over here we are calling the function plus 1 providing it five so let's see here as you can see the argument should be of type option and the type parameter here would be i32 now we have to provide a concrete type for this generic type parameter now again we will cover that in much greater detail but as you can see this is exactly the type we are passing to the function sum is a variant of the option enum and it is holding an i32 value and it is holding an i32 type now then we are matching this argument over here if it is holding a non we will return none if it is holding a sum then over here we are destructuring the value wrapped inside this sum variant into the variable I and then over here we are incrementing I by 1 and wrapping it in a sum because as you can see the return type over here is defined as option i32 and that means that this variable over here will then hold some 6 right because we have incremented 5 over here by one so this will hold an option type and the concrete value would be sum 6. the sum variant and a value of 6. now over here we are calling the plus one function with none meaning the argument will get matched and this will then get returned right a non and as you can see the non is also a variant of the option Ina and this i32 just stands for the sum variant if your turn if we are returning none then this type over here is still valid because the non-variant here doesn't hold any value of generic type t but again generics are a topic on its own and by the way this is exactly the same that we have over here so let's do that annotating the type so over here we have an option holding an i32 type all right and as I've said this is not necessary you can omit that because again the option type is in the prelude then we are calling the plus one function again we are providing it sum 5 which is an option and then we are matching the argument so if the argument is none then we return none it is some then we destructure the value that was passed over here and we incremented by one and return it wrapped in some because the return type has to be of type option that means over here the type annotation would be option i32 and in this case we are passing a non so a non will be returned and by the way the Returns value here would be sum 6 right because 5 plus 1 will be 6. and over here we have non-anti-type annotation would still be option I 32. now we want actually to unwrap this value inside this sum variant and we can do that by using IF let so as you can see we are matching these six variables holding some six and then we can destructure it basically so we can then say sum and the variable n meaning we then can use n over here and I see right now never let this run so we have to pack that into an else block just to ensure that this doesn't run after we are running this conditional here and as you can see it will output the value inside sum now again we'll cover generics will cover a flat and match over here this will actually be the next topic so don't worry if this seems a little difficult to understand we will cover that in much greater detail Implement a linked list via enums now over here this linked list I won't cover because there are a lot of things we haven't seen so in here we have um methods and other things that we didn't really cover so I think this would be more confusing than it would be helpful what I suggest you is that you will come back after we have seen some more topics you come back to this exercise and try to solve it it's not that difficult and the solution will be on my GitHub account but I think this is just Overkill right now so see you in the next topic the normal flow of execution in a program would be from top to bottom line by line but we can actually manipulate the flow of a program and flow control is a concept that refers to the ability to control the order in which statements or instructions are executed in a program it allows to specify which instructions should be executed under which conditions and in what order so we have many ways to do that and some of them are for example using conditionals so we have the if else keywords and match for example and we can use Loops for Loops while Loops there is a special Loop in Rust called Loop we'll cover that and we can even manipulate the flow inside the loop by keywords such as continue and break so let's see so let's start with if else conditionals over here we are initializing n with a value of 5. then over here we have an if conditional checking if n is less than zero in this case this would evaluate to false meaning meaning this this instruction over here will get ignored and the program will continue executing on the next line now in the next line we have another conditional so we can use here the else if keyword and over here we are checking is n bigger than zero in this case this would evaluate to true meaning this line will get printed out now we can also match anything else using the else keyword meaning if this evaluates to false and this evaluates to false then this will get executed now in this case this evaluates to true so this will get executed meaning this over here will be ignored as you can see this was the line that has been printed out number two if else expression can be used in assignments again initializing n and over here we have a variable assignment now we can even use if else conditionals when assigning a value to a variable now remember in a variable assignment is a statement so we so we have to end that with a semicolon like that so let's see we have here an if conditional and this is a Boolean and operation do you remember we have two inputs and both of these inputs have to evaluate to true in order the end operation to return true so let's see we have here n should be less than 10 which would evaluate to True right because 5 is less than 10. and 5 is bigger than -10 so this end operation will return true meaning the meaning the code in this if conditional block will get executed and over here we are just printing out a line and then we are multiplying n by 10 and in fact we don't have a semicolon so this will be returned from this expression meaning it will then be assigned to the variable Big N so big n will then hold a type of i32 now in case this would evaluate to false then this else block would get executed meaning again we are printing out a line and then dividing n by 2.0 now as you can see we should omit this semicolon because this should because this result over here should then be assigned to this variable now we actually can't do this operation here because n is of type i32 and we are dividing it by a floating Point number so we can so we can Typecast that when I 32. and as you can see this would be the output and we are printing out big n meaning 50 over here so n has been multiplied by 10. so let's see we have here a for Loop D4 in construct can be used to iterate through an iterator for example a range A to B now as you can see we have a for Loop declaring a variable n and we provide a range now this range over here reaches from 1 to 100 and 100 here included because we have here the equal sign then we are checking if n is equal to 100 this program will panic that's not what we want so what we can do over here is just removing this equal sign because that means 100 is excluded meaning n will never be equals to 100 the largest it will get will be 99 because let's see all right fix the arrows without adding or removing lines so over here we have a so over here we have an array holding two strings now we want then to iterate over this array meaning this name variable will hold in the first iteration this string and in the second iteration this string so what we can do over here is pointing out name right so this will iterate two times and it will output this and then this now as you can see we are trying to access names again over here and that would then violate the ownership rules because as you can see in a for Loop the for Loop actually will take ownership of the provided value and we can pass here a reference like we have seen before and that means we can use names then again so and by the way we don't have to dereference name over here because the print line statement will do that automatically now over here we have three i32 integers the elements in numbers are copies so there is no move here so we are iterating over each element in this array where n will then hold in each iteration one element from this array so let's see so and this would be the output as you can see in the first iteration name will hold this string and in the second iteration it will hold this string and it prints it out in each iteration then we print out the whole array and over here the same thing but with the integers so again we have an array so iterate the indexing and value in a so there is a useful method called enumerate and this enumerate method takes a collection for example an array or a tube pillar or anything else and it will then return a tuple and this Tuple will then hold the index and T value meaning for example in the first iteration this Tuple will hold for i 0 and for V4 right because at index 0 we have a value of 4 and it will then just print out the index but we increment it by one and the actual value and of course we have to put a into an iterator so let's see like that so we have to first put a into an iterator and then we can call this enumerate method on it because the enumerate method is implemented in the iterator trade we'll take a look at that later so as you can see this will be the output and that was the reason we have incremented I by one because we don't want to start with the zeroest element is four right so we say here the first element is four second three and so on while the while keyword can be used to run a loop when a condition is true fill in the blanks to make the last print Ln work so as you can see we have here a mutual variable n Loop while the condition is true so as you can see this is a while loop and we provided a conditional meaning if this evaluates to true then this Loop will keep running until this conditional evaluates two folds so inside the while loop we have other conditionals as you can see we here check the condition and modulus 15 is 0. if that would evaluate to true then we print out that and by the way this was a question at coding interviews at Google so we are here having more conditionals and a an else conditional so meaning if anything of this fails then this will get printed out so what we can do over here checking if n is less than 10 then this Loop will keep running now when you're using while Loops then you have to ensure that the while loop eventually stops right that this conditioner over here will evaluate to false eventually because otherwise you would have an infinite Loop now to do that we can increment and over here so I increment at the end of this while loop n by 1. meaning at each meaning in each iteration in this while loop n gets incremented by one so n will eventually be bigger or equal to 10 meaning this would evaluate to false meaning we break out of this Loop continue and break use break to break the loop so we have here a for Loop looping from 0 to 100 and 100 included so if n is 66 then we want to break out of the loop anti-break keyword will immediately exit out of this for Loop so when this conditional evaluates to true we immediately exit out meaning the program will continue execution after the for Loop in this case this line over here let's see so as you can see in the end n will be equal to 66. because when I is 66 then we stop iterating continue will skip over the remaining code in current iteration and go to the next iteration so as you can see we are here again iterating from 0 to 100 and we have here an if conditional if n is not equal to 66 then we increment n by 1. and we want them to continue meaning that that the remaining code in this for Loop will get ignored so discontinue keyword the program will go back to the beginning of the for Loop now in case this evaluates to false right so if n is in fact 66 this would evaluate to false meaning we ignore this code block and we go over here where we want to break out of the loo because as you can see we want n to be 66 at the end Loop is usually used together with break or continue so Loop is a special Loop in Rust where this is basically an infinite Loop and you have to manually manipulate the flow of the loop and we will see soon so this is a u32 over here count so let's count until Infinity as you can see we are here using the loop keyword meaning this would keep looping to Infinity or we provide some keywords that will break out of the loop or continue so as you can see in each iteration we are incrementing the count variable by one now if count is equal to 3 then we print out 3 and we want to skip the rest of this iteration so we can use here the continue keyword meaning again the rest over here will be ignored the program will go back to the beginning of the loop so then over here if count is not equal to 3 this will get printed out so we print out the count in every iteration except if count is equal to 3. and then over here if count is equal to 5 then we'll print out okay that's enough and we want to break out meaning count in the end should hold a value of 5. you can see we are iterating one two and then in the third iteration this if conditional evaluates to true meaning we print out three and we continue we are going back to the beginning so the actual value of count over here is not printed as you can see there is no digit 3 but will printed out three over here because that was defined in this code block here and then add iteration 5 we will stop and break out of the loop so Loop is an expression so we can use it with break to return a value and again we have here a counter so over here we have a loop and we are in each iteration incrementing counter by one then we check if counter is equal to 10 then we want to break out but as you notice we want to assign a value to the result variable right this variable should then hold an i32 but we don't return anything here so you can Define after the break keyword a return value meaning for example we want to return counter but in this case result will hold 10 right because counter when we break out will be 10 otherwise this conditional would not evaluate to true so if counter is 10 then we break out but as you can see we want result to be 20. so we can even pass here an operation so we can double the counter value let's see and this is compiling it's possible to break or continue outer Loops when dealing with nested Loops in these cases the loops must be annotated with some label and the label must be passed to the break continuous statement so as you can see we can even Nest different loops so when we do that we have to actually provide labels so we provide here outer for the Outer Loop and inner one for the inner loop and as you can see we have even two inner Loops so we have inner one and inner two alright so let's see in the first inner loop we are checking if count is bigger than or equal to 20. if this is the case we break out of this inner loop otherwise we increment the count by two all right and notice here this would break only the inner one Loop so when we break out of this Loop this outer loop will still continue looping okay so then over here in the outer loop we are incrementing count by five and in this inner two Loop we are then checking if count is bigger than or equal to 30 then we break out of the Outer Loop otherwise if count is smaller than 30 then the outer loop will continue meaning the program will go back to the beginning of the outer loop and continue executing from here so let's go step by step and as you can see we have to find out here what the count value will be in the end okay let's go step by step we start at zero then over here we are checking is Count bigger than or equal to 20 no right so that means count will get incremented by two and this will continue because we are in this Loop remember and this will continue until this evaluates to true meaning when count is 20 then we break out of this inner loop meaning we continue executing here then count would be 20 and we increment it by 5. then we enter this inner two Loop so 25 is not bigger or not equal to 30 meaning we don't break off outer but we will continue outer meaning we go back over here now we enter this Loop here and 25 is bigger than 20 so we break out of this Inner Loop and continue here then we add 5 to count meaning count is now 30. so we enter this inner two Loop and this will now evaluate to true because count is equal to 30 meaning we break out of the outer loop so in this case count will be 30 in the end and this succeeded and I will see you in the next topic pattern match is a powerful construct that allows you to compare a value against a set of patterns and then execute different code based on which pattern matches and these patterns can be made up of literal values variable names wild cards and so on and in a match statement all possible cases must be handled and this is enforced by the compiler and really this match keyword is something that I really miss when I'm coding in other languages so once you get used to it you will love it and you will see why so let's see an example over here we have in coin enum and this enum holds variance of different denominations of U.S currency or U.S coins and here we have a function value in sense this takes as an argument a variant of type coin then it matches the argument and as you can see it matches the variance of this coin enum meaning if we provide an instance of this coin enum which holds a penny variant for example then it will return one right and this will then be the return value of this function so for each denomination over here it will return its proper value then let's see if let so I've said that in a match statement all possible cases have to be handled and this could sometimes lead to annoying boiler play code that is not really that necessary so for example we have over here an option type right a value wrapped inside sum and then we match this variable over here that holds this option type and then we are matching as you can see if it is sum then we the structure the value inside this Max variable and then we just print it out now as you can see we also have to match any other case meaning if it is not some in that case none then nothing will get executed meaning it will return a unit type now this is sometimes two verbose especially for cases like this for example you just want to unwrap something from a sum variant now what we can do instead is just using using E flat so it's exactly the same as this match statement but we know that config Max holds a sum value right so we don't have to so we don't have to consider the case where it holds a non-value and as you can see this is how we would destructure this value into the variable Max we will see a flat keyword and then the pattern so if config Max holds a sum then the value will get the structured let's do exercises fill the blanks so over here we have a direction enum holding directions over here as variance then over here as you can see we are instantiating the direction enum with a variant of the South so the type over here would be Direction then we match this variable here and if this variable over here holds Direction East then it will print out east now we want over here to match south or north so what we can do over here is pattern matching either south or north and we can do it like that so we are here matching so we are here basically saying if this variable holds either a variant of south or north then it will print out south or north now in case it's not any of these variants then it must be West right so we can print out here West let's see and as you can see the variable holds South so this will get matched so this will get printed out match is an expression so we can use it in assignments over here we have a Boolean value fill the blank with a match expression Boolean true binary one Boolean false binary zero so what we want to do is we want to match this variable over here and then we provided the cases so if it is true then we should return one otherwise we return zero so that means the return value over here will then be assigned to this binary variable let's store that as u8 and that means binary should then hold one right because we are matching here true and then the return value should be 1. using match to get the data and enum variant holds again we have the message enum we have seen that before then over here we have an array of message variants over here we are iterating over this array and passing it to this function as you can see the argument should be of type message so we are then matching this provided argument so we want to match over here message move now as you can see the message move has struck like syntax so we have to match it like that and and as you can see we want the variables A and B so let's do that as you can see it holds Fields X and Y and that means we will then destructure these values into the variables into the variables A and B so a should hold 1 and B should hold three then we match over here the message change color variant as you can see this is a tuple-like variant and this Tuple has three elements so we can provide it some variables and we use here RGB red green blue and this is for color representation so in that case G the second value should be 255. and the last value B should be in this case 0. and again because we have to match all the cases if there is anything else that doesn't match here we will print out no data in this parents like that matches matches looks like match but can do something different so the macro matches allows us to test for example this array if a pattern matches so as you can see we are iterating over the elements of this array so over here we have the assert macro and all this does is it asserts that the given argument evaluates to true so we can over here use team matches macro and we provide it a b meaning the character we are iterating over and then we can give it a pattern it should match so as we are iterating over each element over here it should match each of these characters and we can do that like this so we want to match characters from a to and including z or a to and including that lowercase or zero two nine and notice these are characters and not integers because we have here an array of characters let's see and this succeeds as you can see all we did is we iterated over each element and we checked over here for each element if it matches if it matches this pattern and in this case everything matched because assert didn't Panic right because if one of them would evaluate to false insert will cause the program to panic let's see exercise five so over here we have an enum with two variants full and Bar then here we have a count variable which is initialized with zero and over here we have a vector now we didn't cover vectors yet but they are very similar to arrays but their size can shrink and grow meaning during a vector is dynamic in its size so as you can see we are having a vector of my enum variance two of them fool and one bar so this is a vector holding types of my inner then over here we are iterating over the elements of this vector now we want to match my enum Foo and if conditionals don't allow to match for patterns so we can't use that here but we can do something else we can hear instead using matches macro meaning we take e the element in the vector we are iterating over and we match it for this pattern right so if the element we are currently iterating over matches my enum full variant then we want to increment count by one like that and that means count after this for Loop iterating over each element should be two right because matches matched two times because there are two my enum full variants in this vector if let for some cases when matching enums match is too heavy we can use if let instead so over here as you can see we have an option of type i32 we move the whole match block using IF let instead so as you can see match is really verbal so we can use E flat instead and we do that like this we use the E flat keyword and then the pattern we want to match in this case sum and the variable should be I so the inner value here will get the structured into this variable I and then we are matching on all and if this matches then we want to print out these two lines as you can see this is much cleaner and this will get printed out this is a really long string and seven as you can see we here provide I and I just holds the inner value of this sum variant fill in a blank we have here a full enum with a bar variant holding a type of u8 then over here we have an instance of the bar variant holding a value of one now over here again we could do the same thing we can use E flat and then we match the pattern so if the pattern is full bar then we want to destructure the inner value to a variable I and we are matching on a as you can see this compiles so we have a full Anum with three variants and only the last one holds a value so over here we instantiate the full enum with a variant of cues and a value of 10. I hope I pronounce it correctly so remove the codes below using match instead so if I run this code then this would actually work because we are using IF let here but we can do it in a match statement so we match on a right and then we can provide the pattern so in case of full bar then we would then we will print out this line in case of Foo Bass we want to Output this line and in case of anything else like that we print out this line now I can delete that and as you can see much cleaner so in this case match others shadowing fix the arrows in place again we have here an option with an inner value of i32 then we are here using E flat to destructure the inner value to H and that means we are actually shadowing H because as you can see this variable here has the name H and we are then destructuring into a variable h so that means H has been shadowed create a new variable with the same name as previous age now over here as you can see H would be sum 30 which is wrong because we are actually here destructuring it right so H doesn't hold some 30 anymore but it will hold 30 because we are here destructuring it the new variable H goes out of scope here right so this variable over here goes out of scope and image can also Shadow a variable so we can here essentially doing the exactly same thing like in the uh like in the E flat statement over here foreign this would be the output from this match here alright see in the next topic so let's see patterns we can use this sign to match several values and we can use this one to match an inclusive range so as you can see we have here a function match number which takes an integer as argument then we are matching this integer and if it is one then we will then we'll print out one and fill in a blank with this don't use ranges okay so we have to match here two to five so we can do it like that and this pattern is basically saying if n is either 2 or 3 or 4 or 5 then we will print this match an inclusive range so if the so if n is between 6 and 10 where 10 is included then it will print out that and for any other case because again because again in a match statement we have to match every case so in this case we will print out that the add operator lets us create a variable that holds a value at the same time we are testing that value to see whether it matches a pattern so let's see we have here the point struct that has two Fields with I 32 types fill in the blank to let P match the second arm so we have to complete here something we have here an instance of the point type now let's see the match here so we match on P and as you can see this is how we would match a struct so we provide this track name and then the fields as you can see we are matching X and Y so so if Y is equal to 0 that means this arm here matches and this will get printed out and we then can use x over because this is actually a destructuring assignment so this would be the same as writing it like that but this is shorthand syntax so we are then using X here now in the second arm as you can see we are matching X and Y Fields so X should be between 0 and 5 5 included and Y should be either 10 20 or 30. now as you can see when we are matching here this pattern we can't use x we don't use x here but we can in fact use y because we are using here the add operator so this means we are destructuring the variable y of the field y so that means the value that Y is holding over here will gets we'll get this structured into the Y variable and at the same time we are testing why if it is matching this pattern and the last arm as you can see we don't match for any pattern other than this main pattern and then we can use X and Y because again this would be the same as writing it like that all right now we want to match the second arm that means we have to provide values that match these patterns so let's see X between 0 and 5 so let's do three and Y 10 20 or 30 I will do 30. as you can see this second arm matched fix the arrows over here we have the message enum that's holding which is holding one variant with a struct like syntax as you can see this is one field ID of type i32 then over here we are instantiating this variant so this will be an instance of the message enum and then over here we are matching this instance so if it is hello then we match the ID field so is it between 3 and 7 then we want to output here the ID now the problem here is that we did not actually destructure this field here into its distinct variable right so we have to provide here each variable let's do ID because that's the one defined here and for that we have to use the add operator so we are destructuring and matching the pattern at the same time and over here this is already done as you can see we can also provide any a variable with another name as you can see so the value of the ID field then we'll get this structured into this new ID variable right it doesn't have to be the same variable name as the field we have and again we are destructuring and at the same time pattern matching and that's and that is the reason we are using the add operator and as you can see when we are matching like that then this ID over here got this structured because this is the shorthand for this all right and when we have a pattern here we have to wrap it inside parentheses that's also important and as you can see in this case the first arm matched right because ID over here the value is between 3 and 7. in match card is an additional if condition specified after the pattern in a match arm that must also match along with the pattern matching for that arm to be chosen fill in the blank to make the code work split must be used alright as you can see we have here an option with an i32 value then we have here split and we are here matching the num variable and we are doing that to basically restructure the inner value so we are putting the value of 4 into X right so we can then use x over here but we over here also want a match card to check if x in this case this value over here is less than split we can do that like this foreign so this basically is just for destructuring the this value into X and over here we have an additional conditional in match card to check for a more specific condition to be true so if num is a sum so if num is of some variant then we the structure X and check it again and if all of that matches then we execute this if it is a sum but yeah but the value over here is actually bigger than split then this will get executed and if num holds a none then we just return a unit type ignoring remaining parts of the value with with this so as you can see we have here a tuple I want annotate that so we are matching on numbers now we only care about the first as you can see and the last one so what we can do over here we are matching on a tuple right so this is a tuple and we want to destructure the first and the last values into the variables first and last we can do it like that right so we have the first then anything in between and then the last so first should all two and last should hold 2048. using pattern mutable reference to V to match a mutable reference needs you to be very careful due to B being a value after matching let's see fix the arrow with least changing don't remove any code lines so over here we have a mutable variable holding a string and this would be a variable that holds a mutable reference to a string now we match r and that means over here we can destructure it directly into a value like that so this string over here will then be destructured into the value directly we don't have to match mute the reference like that right and that means the value variable will hold immutable reference to a string and of course then we can modify this stream over here all right that's it see you in the next one a method is a function that is associated with a particular type or struct it takes parameters and returns a value like a function but it's defined as a member of a struct or an enum it's called using dot notation like accessing members of a struct and it's implemented through an implementation block let's see an example over here we have the struct rectangle with two fields and as you can see we can then Implement methods for this rectangle type using the input keyword and the name of the type we want to implement the methods for so then over here we can see that rectangle has a method called area it takes as an argument a reference to self and self stands for the name of the instance so inside the method we are multiplying self dot with this field by self dot height this field and then we return the result of this operation now again self over here will be replaced by the name of the instance so when we create an instance of rectangle with concrete values then as you can see we can call this method on the created instance right one meaning when this method is called like that then self over here will be replaced by rect one so here rect1 dot width times rect1 dot height meaning 30 by 50 will be returned now let's see Associated functions an Associated function is a function that is associated with a struct or an enum but doesn't take an instance as its first parameter so you can recognize an Associated function if it doesn't take self as its first parameter it's called using the name of the type not an instance of it and it's often used as Constructors for a struct or an enum so let's see an Associated function as you can see we have again the rectangle type and we Implement over here again in an implementation block the new Associated function for the rectangle type and notice we don't have a self here and you will see in a minute why that is now the new Associated function takes two arguments with and height both of type u32 and it will then create an instance rectangle and returns it meaning when we want to call this Associated function we don't call it on an instance right because we don't have self here we call it directly on the type so in this case we take rectangle double colon and then the function new and we provide it two values 5 and 10 right that means Rec 1 will then hold an instance of the type rectangle okay let's do the exercises we will skip this example because I've showed you that before and we will do the exercises directly methods are similar to functions declare with FN have parameters and a return value unlike functions methods are defined within the context of a struct or an enum or a trade object and the first parameter is always self which represents the instance of destruct the method is being called upon so as you can see we have the rectangle struct again and over here we have the implementation block we can implement the methods for this type now as you can see we instantiate rectangle over here with concrete values and then assign it to right one select one holds an instance of type rectangle then over here we call this then over here we call the method area on rect one and we should get back 1500 so let's implement the area method so the first thing is we always start with cell so every method takes as first argument self okay then the return type will be Au 32 right because we are here doing operation with these two fields now what we want to do to get the area of the rectangle is multiplying the width by the height so we use self.width times self dot height and I omit here the semicolon so this will get returned let's see so when we so when we call the area method on the rect one instance then rect one dot width times rect1 dot height will result will result in 1500 meaning 30 times 50. self will take the ownership of current struct instance however reference to self will only borrow a reference from the instance Let's see we have again destruct traffic light with one field now over here there is the implementation block we can Implement methods for destruct Now using self to fill in the blank so we have been using self like this right meaning we take a reference to the instance now this is actually syntactic sugar for this and as you can see itself with an uppercase s refers to the type we are implementing the method on right so in this implementation block when we use self uppercase it refers to traffic light and you can use this syntax or you can use like we did before using just self right and here we want to take immutable reference because we are here mutating the color field and again and this and this and that are exactly the same but this is not mutable so if I would do it like that then as you can see these are actually exactly the same thing but I would suggest just stick with that it's more readable and it's clear what you mean Associated functions all functions Define within an input block are called Associated functions because they are associated with the type named after the impul we can Define Associated functions that don't have self as their first parameter and thus are not methods because they don't need an instance of the type to work with so as you can see we again use the traffic like struct and here we have the imple block so let's see first Implement an Associated function new it will return a traffic light contains color red must use self don't use traffic light in FN signatures or body let's first see in main what's going on so we call here the new Associated function and disassociated function is associated with the traffic light type and again notice the difference is we call this directly on the type and not on an instance otherwise it would be a method so we call new and then we want to have an instance of traffic light back write this new function should return an instance of traffic light meaning we can then call upon this instance light the get State method and return Red so let's see over here we are expecting no argument right so let's leave it like that and remember Associated functions don't take self now the return type would be traffic light instance and because we should use self in here we can do that and again self in an implementation block refers to the type in this case traffic light then in there we want to create an instance of traffic light so again we can use self instead of this name and then we like normally instantiating it providing it concrete values so in this case we want this self.color field to hold a string of red like that and as you can see then we should get and as you can see when we call this Associated function we'll get back an instance with this concrete value a string red then we call upon this instance light the get State method so again self here refers to light because we are calling the method on this instance and it takes just a reference to the instance meaning light is usable even after this method call and all it does it will return the value inside the color field of the instance in this case slide so light dot color will hold a string of red multiple input blocks each struct is allowed to have multiple input blocks again rectangle struct and using multiple input blocks to rewrite the code below so we can either put all of the functions and methods in the same input block or we can even reorganize it using various different input blocks so we can for example put that in its own implementation block like that and it will work exactly the same this is just for organizing and restructuring enums we can also Implement methods for enums so over here we have the enum traffic light color with three variants Implement traffic light color with a method so over here in main we are instantiating traffic light color with a variant of yellow this would be the type annotation and then when we call a method color on the C instance it should return yellow so let's see so we could Implement that using a match statement then we match a self and if match self would be this then we return the string yellow okay and we can over here instead of writing the whole name of the type we can again use uppercase self referring to the type this method is implemented on in this case traffic light color and remember in a match statement we have to handle all the possible cases meaning we have to create an arm for right and for green two so let's do that let's see and as you can see because we have here an instance which holds the variant yellow we will get back yellow when we call the color method all right that's it until next time until now we have only been dealing with concrete types but we can also deal with generics and generics are placeholders for concrete types it enables writing more reusable and flexible code and it avoids having duplicate code for different types it's a zero cost abstraction meaning the rust compiler will add compile time fill out the generics with concrete types and there is also a concept called const generics I will just briefly touch upon it because this is more of an advanced topic but I will show you one exercise just so you get the idea from it so const generic is a type parameter that represents a compile time constant value it allows to write generic code that operates on values that are known at compiled time and it's mainly used for array sizes bitwits and other constants and you will see what I mean when you see the exercise let's start with generics so as you can see over here we have a struct a now this would be considered a concrete type because we don't deal with generic types in here now destruct s takes as an argument an instance of destruct a this is also considered a concrete type because again there are not generics used here now destruct s-gen is considered a generic type because we are dealing here with a generic type parameter and this would be denotation when you declare a type for example then after the name of the type you annotate a generic type parameter like that using these symbols over here and then providing a name and by convention this is usually T standing for type and that means this struct can hold any type now as you can see we have here some functions this function over here takes s which is a concrete type the Gen spec takes as an argument an instance of s gen with a type of a now again a would be a concrete type meaning the type parameter over here is specified to be a concrete type so we can only pass to this S10 struct an instance of destruct a then over here we have the genspec i32 function which takes as an argument the S-10 struct over here with a value that is of type i32 and over here we have a generic function meaning we are declaring after the function name the the generic type parameter T and then we can use it on here meaning that we can provide as an argument over here an instance of S-10 with any type so let's see using the non-generic functions so these over here are all non-generic as you can see because we didn't Define a generic type parameter and all the values it is using are concrete right over here we have S over here we have S10 and this is taking as a value T over here the concrete Type A and over here it takes as concrete type i32 so all these are non-generic functions the only generic function over here would be this one so let's see so we want to call Direct F and function and as you can see in the function signature we are taking as an argument a type of s so let's provide that now we have to pass into s the concrete type a right because that is defined in this struct then gen spec t so this function over here takes an S10 struct with a type of a now again a would be a concrete type so we provide here S10 and a right so we are passing to the S gen struct over here a value of type A and in this case we want it to be S10 and let's say 7 because over here we Define that this function should take as an argument a type S10 which holds an i32 integer then explicitly specify type parameter chart to generic so over here we are calling the generic function meaning we can pass to it and S10 type holding any possible type now we can explicitly specify a type parameter for this T over here and we are here saying T should be a char meaning we are calling the generic function over here with the type S gen and passing as a value a type of char let's say a and implicitly specify type parameter chart to generic so we don't have to annotate the type here it will be automatically inferred by the compiler so if I do the same thing over here let's go with Z this time as you can see this is compiling now again because sgen is defined to be generic over its type pass any value so let's pass for example a floating point as you can see this is also compiling because T over here is a placeholder that stands for any type possible but again when we annotate the specific type then we can only pass a character in this example so if I try 7.7 here this would be in compiler error if I Define f64 as you can see then it's working all right a function call with explicitly specified type parameter looks like that and this is also called the turbo fish syntax so let's see implement the generic function below so as you can see we are calling here the sum function with different types and that's the whole point of generics so here we are calling sum with two I8 integers over here we call this sum function with 2i32 integers remember if nothing is annotated then the default integer type is i32 and here the same thing but for floating Point f 64. so we have to use here generics otherwise this won't be possible over here we are creating our generic type parameter and again by convention this is called t then we take here the arguments so let's say a should be of type T and B should also be of type T and then we return a type of t meaning if we pass here to i8 integers then T will get converted to an I8 right this is just a placeholder and because we operate then on two i8s the type the return type will also be I8 so what we do is just adding together the two arguments and return it and over here we have to implement a trade bound and we'll cover that in the next section so we have to Define here that t should implement the add trait otherwise we can't use this plus sign over here and again we will cover that in the next section when we look at trades and as you can see this is compiling so we can pass to sum two numbers of any type let's see number three Implement struct point to make it work as you can see we are instantiating two point structs but over here we are passing to the fields different types so in the first instance we would have i32 values in the second instance we are dealing with floats so to make that possible we have to use generic so let's implement the point struct and we are defining here the generic type parameter and then we can Define the fields and as you can see the fields hold the type t meaning if we pass it intervals then X and Y Must both be integers if we are passing it floats then both of these fields must be of type floats now because we are here defining a generic type parameter this parameter over here becomes part of the type annotation so over here we are providing to the fields of the point struct values of type i32 so we must annotate that and over here we are passing f64 and as you can see this is compiling now again we don't have to do the type annotations actually I just want to make that clear as you can see the compiler is able to infer that for us modify the struct to make the code work don't modify this code so as you can see we are here instantiating a point but the fields over here hold different types so this is not working because T over here defines that both of these placeholders will be filled with the same concrete type so in this case if we are passing X over here with an i32 type then the compiler thinks that y should also hold an i32 now what we can do then is defining another generic type parameter and by convention you just go one letter further so T and U meaning we can then use here U right and this indicates that the fields over here hold different types or could potentially hold different types they can also hold the same types and then when we annotate that we have said that the generic type parameter becomes part of the type annotation so we would pass here for x so we would pass here for T and i32 the value the type the X field is holding and over here a string so this would be the type annotation for this returned instance number five at generic for well to make the code work don't modify the code in Main so here we have the valve struct holding one field and we have an implementation block implementing one method for the well type now how do I know it's a method because it has as first parameter this self keyword and all this Value method does is just returning a reference to the Bell field value all right so when we instantiate the Bell struct over here we are providing a type of f64. but over here we are instantiating the valve field with a string so again we have different types so let's annotate here the generic type parameter then we can use T here meaning the Field Val can hold any type so let's annotate here now over here as you can see we are returning the value of the Val field and we Define here that it should be of type f64. now this would cause an error because when we call over here the Value method on the instance of Y then self.wall would hold a string right so it doesn't return an f64. now we have to use a generic type parameter also in this implementation block and we can do that like that so we have over here the generic type parameter for our type and as we've said this becomes part of the type annotation and over here we are also defining a generic type parameter T so we are then able to use it inside the implementation block so over here we can Define that it should be a reference to T right a reference to any type that the wall field holds so in the first call of value it would be an F 64 right that is the type of The Returned value that Val field is holding and in the second call over here it would be a string right so T then would be string number six we have here a point struct and we are here defining that the fields could potentially hold different types but they could also be the same and here we have the implementation block for our point struct now as you can see over here we are instantiating a point with I 32 values right so they can be the same even if we here have two generic type parameters it doesn't matter they can be the same but over here we are instantiating point with two different types right we are instantiating point with a string literal this here and HR that over here so they are different in its types then we call over here the mix up method on P1 and provided an argument of P2 and then over here as you can see we want then P3 to look something like that so we want the newly created instance here P3 to hold the value of the first instance in this case P1 as you can see we are taking X from P1 and we want the Y field to hold the value of the P2 instance of the P2 instance this character right so how would we do that with generic type parameters now let's first Implement here the mix up function and again the convention is just the more generic type parameters you are declaring you just go further in the alphabet so T U V W all right and as you can see as a first argument we are taking P1 now let's take ownership we don't care because we don't use P1 after this point meaning we can take ownership and we don't have to use a reference to self here and as an argument here we want P2 meaning a point with generic type parameters we are declaring here and the return type will be a point with a generic type parameter t and a generic type parameter of w and you will see why that is so then what we want to do is creating here a new instance and we provide here x which should be of self because self here refers to P1 right so we want self and we want the x value and Y should take it from the second argument the P1 over here and we want the Y field so let me actually annotate that as order right we take here another Point instance and this would refer to P2 because we are passing P2 over here to this mix up method so we take from P to d y field and then we are returning this instance meaning P3 will then look something like that right so let me annotate the type we would have a point with the x value of P1 and i32 and a character type of P2 the Y field right and that is what we will get let's first see if this even compiles and this is compiling now as you can see we have here a lot of generic type parameters so when we take a look at the mix-up method as you can see we are here declaring new generic type parameters because we want to Define over here that the types self and self again refers to the instance over here P1 that these types are different from the types provided on the second argument the instance of P2 right if we compare it together then P1 instance holds different types than the P2 instant so we need new generic type parameters because t and u are for this P1 instance and the other the argument we are here providing P2 will hold different types right and as you can see we are then taking from P1 the X field and from P2 we take the Y field and this would be the return type a point with t this is from P1 X and W this would be the second type from DP to point all right fix the errors to make the code work as you can see we have here is tracked point which has two fields which are generic over its type and we Implement over here a method for the point struct over here now this method over here is in fact only implemented for a struct point that holds F 32 types of fields right so if we would pass floats then this method is not implemented for a point so this is only implemented for point with a concrete type of F32 now then over here we are calling on the fields X the power integer method and let's see that in the rust documentation so as you can see the power integer method over here is actually implemented for f 64 types right so self in this case must be of type f 64. as you can see over here we have X which holds an F 64 type so we can we can call this method on X right again this is a method and not a function so the problem over here is that self in this case would be F32 which is not the right type so let's change that f64 and that means self over here would be of type f64 the concrete type we are providing here and then of course the return type would be f64 right because we are here performing an operation on two f64 types now over here of course we have to provide floating Point values right because again this method is only implemented for a point that holds as Fields F 64 types like that all right and then we can use this method as defined in the standard Library and of course over here we just have to annotate one type right because we only have one generic type parameter and as you can see this is compiling and I will see you next time so I've told you that we will take a brief look at const generics and I will just solve one exercise and leave it at that because constant generics is a more advanced topic and you don't need it that much in your everyday programming so they're useful at some situations but they are not that much used so let's see an example over here we have here a struct array so we have here destruct array which defines two generic type parameters t and n now T is a generic type parameter and n over here is a const generic type parameter and as you can see each array struct holds a data field with an array of type t and n elements all right so when we look over here we have an array and I will now annotate this type of type array right this struct distract we have defined over here we are holding three instances as you can see one two three three instances of this array struct right like that now I've told you that when we have generic type parameters it becomes part of its annotations we have to annotate it over here too in fact we don't have to do that because it's usually inferred by the compiler but I will do it right now so this over here cares about the types in the aerated data field holds so as you can see we have here integers we have here floats and over here we again have integers and what that means is that in this array over here these actually should all hold the same type because we have to provide here a concrete type right for T and that means this data field over here will then hold in its array this type so in this case we will do i32 and we need to provide it a constant meaning for the length of the array in this case we Define three all right and what that means is that the arrays inside the data field of these instances must be of i32 type and they must have a length of 3. right so we can't use floats here we have to use i32 and over here we need to add another element now they can't be different for example let's do it like that that is possible but all of them over here must have the same type and the same amount of elements because we are annotating that here so it complies with the generic type parameter annotations here now let's create another array with floats now I want this to hold array instances of type f64 and let's do two elements and I will pass here let's say also three and that means inside here let's first look at the outside array at this array over here so we want three elements of type array this array struct right so let's define that like that as you can see we have here three elements of type array now we Define here for the generic type parameters that the data field the data field should hold an array right because this is defined over here in the array struct so each data field should hold an array of f64 right the T over here of two elements right this is the const generic parameter so we are passing here a constant and not a type so in this case we the data field would have concrete types of f64 and two elements right so let's fill that out like that as you can see this is compiling because we are here complying with the generic type parameters we have three array instances right and each array instance holds a data field with these types f64 and they have to be two elements inside the array all right see in the next topic we will now cover trades so a trade is a set of methods that can be implemented for multiple types in order to provide common functionality and behavior between them a trade consists only of method signatures which then have to be implemented by the Target type it's similar to classes in other languages but not quite the same and it defines shared behavior in an abstract way so let's see how that might look like as you can see over here we have two custom types sheep and Cal now we want to indicate that these are somewhat related so over here we have created a trade anime which defines one method then over here we are implementing the animal trade for sheep and for cow now when we Implement a trade for some type then we have to implement all the methods the trade defines so in this case as you can see in the trade animal we only provide it with a function signature and then the concrete types for example sheep and cow will implement the method and Its Behavior so as you can see both sheep and cow then implement the animal trait meaning they automatically implement the method right so both of them take a reference to its instance for example an a ship instance and then just return the string over here and the same thing for cow now there is also derivable traits and we have seen that before a trait can be automatically implemented for a struct or an enum by the rust compiler these are called derivable because they can beat derived automatically and the most common derivable traits are debug allowing to Output content via this debug notation we have seen that clone which enables a type to be duplicated with the Clone method copy enables a type to be copied implicitly without requiring explicit clone method and partially queue enables comparison now we can pass trades as parameters so the notify function over here takes as an argument any type that implements the summary trade meaning we can ensure that the item argument over here implements this summarize method right if we provide here A type that didn't implement this summary trait then it wouldn't have this summarize method trades can be used as parameters for functions the function notify takes as an argument any type that has implemented the summary trait now there is also a concept called trade bounce meaning if we declare over here a generic type parameter we can define that the type over here must implement the summary trade so the argument we pass to this notify function must implement this summary trait and this is similar to the example using the impulse summary but more verbose so as you can see this would actually be exactly the same like we did over here trade bounds are declared like generics after the name of the function and use trade bounds if you have lots of parameters to avoid this so if we would have here an argument which takes a typed Implement summary and another argument which again takes a type that implements summary we can do it like that so we Define here a trade bound meaning the generic type T must implement this summary trait so both argument 1 and argument 2 take a reference to a type that implements the summary trait and if you have big trade bounds like for example this we have here generic type parameter T and we say here that the type T has to implement the display and decline trades and the generic Type U has to implement the Clone anti-debug trait right and then over here as you can see the first argument must be of type T and the second one of Type U and we are taking references so we can write that in a more concise way using a verb Clause meaning after the return type in a function we can use the verb keyword and then over here Define our trade bounds and this over here is exactly the same as this but instead of defining the trade bounds directly on the generic type parameter we do it in a separate block foreign and we can also return from a function a type that implements a specific trait so as you can see the return dog function over here returns an instance of the dog struct and because the animal trade over here has been implemented for torque we can also Define the return type as follows we return here an instance of talk meaning we return here a type that implements the animal trade because as you can see animal is implemented for the dog struct let's see exercise one feeling the two input blocks to make the chord work don't modify the code in main now we have here a trade hello finding two methods now over here you can see that we didn't only provide the function signature but we implemented basically the whole method why is that because this is considered the default implementation meaning any type that implements the hello trade automatically gets this default implementation of this method so we don't have then to implement it manually it will automatically get implemented for the type that implements the hello trade but as you can see over here we have a method which is here just a function signature so we have to implement that manually then over here we have two structs student and teacher and we Implement over here the hello trade for student and for teacher so let's see we have here an instance of student and as you can see this is just a struct that doesn't hold any values right so we instantiate it like that but because we have here implemented hello for the student struct that means we can then call upon it this method over here as you can see we call this say hi method on the S instance and then it should return high now we don't have to implement this say hi method over here again like that we don't have to do that because it's already implemented by default right but what we have to implement is to say something method because as you can see we are calling the say something method on the instance of s and remember when we Implement a trade for a type in this case student we have to implement all the methods except the default implementation here so let's actually take this and copy it over because the function signature must be exactly the same like defined in the trade and then over here we want the return to be I'm a good student so let's return this string here like that so when we call on an instance of student this say something method then it will return I'm a good student now over here we are instantiating the teacher struct and as you can see we are calling the method on the T instance and this should be the returned string so as you can see this is different from what is implemented in the default implementation and that means we have to re-implement this say hi method for the teacher type and the return string should be hi I'm your new teacher so as you can see if a trade defines a default implementation we can also overwrite the default implementation and provide another return value and of course because we have to implement this over here I will copy that and I will implement it for the teacher type so as you can see when we call the say something method on an instance of teacher then we should get back this string so as you can see we have then implemented the hello trade for our custom types student and teacher let's see if that compiles and this is compiling derived the compiler is capable of providing basic implementations for some trades via ddrive attribute for more info please visit here let's see over here we have a tuple struct centimeters which holds a value of type f64 now as you can see this truck here derives to trades partially q and partial order meaning the compiler will automatically Implement these trades for our type centimeters now partially Q is for comparing equality and partial order is for ordering so over here we have inches and and inches is a tuple struct that holds an i32 type and it derives the debug trade meaning we can print it out using the debug notation and as you can see over here we Implement a method two centimeters for an instance of the inches type so when we call this method on an instance of inches then we will convert it to centimeters right so we hear the reference self meaning the instance of inches and then we destructure over here the inches basically the value that the Tuple struct inches holes so this variable then will hold the value of the instance provided here okay and then as you can see we are multiplying inches by 2.54 to convert it to centimeters and we are wrapping that and we are returning that as an instance of centimeters as you can see add some attributes to make the code work don't modify other code so as you can see over here we are instantiating this seconds struct so then we are printing it out using debug notation so we have to derive over here the debug trade then we are here comparing for equality so we have to derive The partially Q trait and over here we are checking if one second is bigger than one second meaning we need to implement the partial order trade like that and as you can see we are here instantiating the inches struct providing it a value of 12 right this i32 type and then we print it out and let's check debug is implemented so we can print it using debug notation and over here we are instantiating the centimeter struct as you can see centimeters takes a value of f64. and over here we are comparing as you can see over here this foot instance holding an instance of inches with 12. so in the U.S 12 inches equal one foot and then we call the two centimeters method on it mean meaning this will then return an instance of centimeters right so we are taking here inches 12. and that means 12 over here will get destructured right here we have here self would be inches 12 and we take it as an reference like that so we destructure it like that using the let keyword the reference because we want to take the value and not a reference and then 12 will be put inside the inches variable then we are multiplying by 2.54 and returning an instance of centimeters and that means we can then compare this over here with the meter because the meter holds also the same type namely the custom type centimeters over here right so both types would be here of type centimeters and if this is smaller than this then it will assigned to this variable a string smaller otherwise bigger and these are string literals by the way and over here we are outputting the result of this compare let's see and as you can see this is compiling so we are outputting here one second looks like seconds one as you can see we can output this type over here seconds because we have derived the debug trade meaning we can use debug notation then one foot equals inches 12. and over here this one foot is smaller than one meter operator in Rust many of The Operators can be overloaded via trades that is some operators can be used to accomplish different tasks based on their input arguments this is possible because operators are syntactic sugar for method called example the plus operator in a plus b calls the add method as in a at B over here this would be a method this add method is part of the add trade hence the plus operator can be used by any implementer of the add trade so Implement implement the function multiply to make the code work as mentioned above plus needs T to implement standard Ops add trait that's why we are here importing the Ops module from standard Library so let's implement the multiply function as you can see over here we are providing two u8 integers to the multiply function now in the second call over here we are providing flow means we need a generic type parameter like that so this function takes two arguments and it will return T basically the same type of the provided arguments and then over here we basically just want to multiply a by B now as you can see over here we are using the operator for multiplications and that means we have to ensure that the T over here the type of the arguments that the multiply function takes implements the Mal trait now the mall trade like the add trades over here then knows that if both of the types of A and B Implement The Malt rate it is possible to multiply these two together so we can use here a trade bound meaning the type T over here should implement should implement the standard of malt rate because again this is just overloading and syntactic sugar for this call right so this actually would translate to this and we are seen in the add over here that it would be a at B right these operators are just overloaded so it's easier to use and that's why we have to ensure that a this argument here of type T has actually implemented the Mal method and we make sure that this is the case when providing here a trade bound meaning T must Implement The Malt rate meaning then that t has implemented the Mal method let's see and we have here to also find that the output will be of type t because as you can see the mult rate over here has an Associated type of output so we have to Define and we will take a look at Associated types but we have here to Define that the output should of type t that the output should be of type T right let's see again and this is working fix the arrows don't modify the code in main so over here again we are using the Ops module from the standard Library then we have here two structs full and power and full bar and Powerful the standard Ops add trade is used to specify the functionality of the plus operator here we make add bar to trace for addition with a right hand side of type bar the following block implements the operation full plus bar should return powerful so if we USD plus operator with these two types over here we should return this type right full plus bar should return full bar now let's see the add tray now the add trade over here takes as default parameter rhs standing for right hand side if we don't provide anything to right hand side then it will be self meaning the type we implement the add trade on and as you can see we have an Associated type here for the output and over here as you can see it takes self and basically becomes the owner when called on an instance that implements the add trade and right hand side is off type right hand side which means by default it would be self and this looks confusing I know but you will get it when we Implement that over here so as you can see we are implementing from the Ops module the add trade so we can Implement from this standard Library trades for our custom types and let's see again when doing something like that then then the compiler will actually do something like that right it will call the add method now this over here is the type here would be an instance of Foo but just so you get the concept and as you can see we have here to define the right hand side meaning the right hand side over here would be poor and we are implementing the add trade for Foo right because we want the instance to be of Type 4 so we implement it for Foo and we want the right hand side to be of type bar now if we wanted to add for example then we could omit that because you are seeing that the default itself right so the default right hand side would be self as you can see so we can omit that but the bow but that means both types must be the same in this case Foo and fool right because self over here refers to the type we are implement the trade four right but in this case it would be bar right because the right hand side is different from the left hand side and the output should be of type full bar so as and again Associated functions we will cover that very soon so over here we have to implement the add method right because the add trade over here defines an add method so we have to implement it and that means we take self and this is taking ownership of an instance of Foo right and right hand side would be of type bar again as you can see Foo would be the instance then we call the add method upon the instance and this argument should be of type par and then we return full bar so in Main when we call so in main when we perform this over here full plus bar we will get back full bar right so we have implemented the plus operator on our custom types now let's do the same for minus sign but in this case full minus bar would be powerful so let's see as you can see this would be distract we want to return powerful so minus sign is actually the same but implementing this sub trade so we will type full sub R right this would be the sub method defined in this sub trade as you can see we are here implementing the substrate from the Ops module for bar now again we want actually to implement it for Foo because here we call Foo support meaning we would implement it for the foo instance because cell 4 here refers to the instance the method is called upon right so the sub will be called upon self meaning we have to implement it for Foo and the right hand side would be bar now we want to return bar food that's true and over here as you can see the sub method is implemented but as right hand side the type should be bar right this is the right hand side of the operation and we are returning powerful so when we do this full minus 4 we will get back powerful so we have overloaded this operator and of course to do assert EQ here because this is a custom type we have to implement the partial EQ trade so let's do that and we also have to implement the debug trade as you can see this is compiling meaning when we use our custom types with these operators they will return a custom type and we can do that by implementing from the Ops module from the standard Library the appropriate trades for our custom types use trade as function parameters instead of a concrete type for the item parameter we specify the input keyword and the trade name this parameter accepts any type that implements the specified trade as you can see we have here a trade summary Implement function summary to make the code work fix the errors without removing any code line so over here we have a struct post with three fields of type string and we Implement over here this summary trade for the Post struct meaning we have to implement the defined method in the trade so we implement it for post like this as you can see we are accessing from the instance remember this is a method so it refers to an instance it is called upon right so we access the fields self.title and self dot author all right then over here we have a struct Weeble with two fields of type string then we implement the summary trait for Weeble and we then have to implement the method from the trade all right because again when we Implement a trade for a type we have to implement its methods so this over here accesses the username and contact field and prints it out now here we have an instant of post and here we have an instance of weibo so as you can see we are here calling this summary function with an argument of post and weibo so we have to implement this summary function below let's do that now as an argument it takes now as an argument it takes in the first call Post and in the second call Weeble right two different types but now think about it what do they share in common write they implement this summary trade right we have implemented it for post and for Weeble so we can Define over here that the argument provided must be a type that implements the summary trait and we do it like that and then inside the function body we want to take this argument and again a is an instance of a type that implements the summary trait so we are taking a and we call upon it the summarize method meaning the first one it will be post dot summarize now post over here is of type post meaning we go to post and as you can see this would be the summarize method as you can see self would refer to the instance in this case post this instance over here so then the summarize method will return a string so let's actually assign this to a variable this would then hold this string and we can then output like that let's see and of course when we are calling the summary method with an instance then we can't use the instance again so let's define here that it should take a reference right and as you can see this is compiling now what we can do over here and this would be exactly the same we can Define here a generic type parameter T and here Define that it should Implement summary right so we Define here the type t and it should be a type that must Implement summary and then we can write it like that so a should then be a reference of type T which implements summary trait let's see as you can see exactly the same thing returning types that Implement rates we can also use the infiltrate syntax in the return position to return a value of some type that implements a trade however you can only use input trade if you're returning a single type use trade objects instead when you really need to return several types so over here we have two custom types sheep and Cal and we have one trade animal which has one method so over here we are then implementing the animal trades for both of these custom types and as you can see when we Implement a trade for a concrete type then we have to implement the given method so over here we have a function random animal it takes a random number and then and then looks if the random number is less than 0.5 it will return a sheep instance otherwise it will return a cow instance now over here the return type of the function would be a type that implements the animal trade which both of them do and as you can see in main we have here our random number and we call this random number on the random animal function so let's for now assume that this really is random okay so that means that when this is passed over here it could either be a sheep or a cow to be returned here but the problem here is that the return type in a function must be of known fixed size at compile time which in this case sheep might be bigger than cow or cow might be bigger than sheep so the exact size is not known at compile time so when we do that this doesn't work and to make that work we have to know about trade objects so let's see so trade objects using input traits doesn't work when returning multiple types different implementations of a trade probably with different amounts of memory but sizes of types must be known at compiled time in this case a trade object can be used a trait object is essentially a pointer to any type that implements the given trade where the precise type can only be known at runtime so over here for example we have a trade animal and two custom types token cat then we implement the trade animal for both of these types and as you can see we have a function return animal it takes a string and if this string is dark then it returns a dog instance now if it returns cat then it returns a cat instant but as you can see we are here providing a reference to the instance and not the incense itself because when we return a reference to a type that implements the animal trade it means that the size of this type is known at compile time because remember a reference is a pointer and the size of a power and the size of a pointer is known at compile time meaning it's of Type U size and U size is 8 bytes on a 64-bit computer so the size of the return type is known it's just a pointer sized type so the size of the return type is known at compile time it's of size U size right because we are here returning a pointer meaning it doesn't matter if dog is bigger than cat or otherwise it's always the same size because pointers have a fixed size at compile time and that is how we would solve this problem so again here we have a function which so here we have a function which returns a type that implements the animal trade this could be dog or cat as the trade object is behind the pointer the size is known at compile time which is U size the size of a pointer this allows for more flexible code as the exact return type doesn't have to be known at compile time as long as the size is fixed so we don't care anymore about the size of dog and cat because they're behind a pointer and to understand what's going on you have to know about static and dynamic dispatch so let's first look at static dispatch static dispatch resolves method calls at compile time the compiler will generate function code for each concrete type that implements the trade it calls the appropriate function based on concrete types it's faster and more efficient than Dynamic dispatch but it doesn't provide great flexibility so we have here the trade animal holding one method say hi then we have Talking Cat and we implement the animal trade for dog and for cat now as you can see when we create an instance from dog and from cat and call the say hi method on it because as you can see every every type that implements the animal trait must have this method and when we then run this program then the compiler will actually generate methods for each concrete type in this case stock or cat because then the say hi method we are calling here on the dog and cat instance is known at compile time so the compiler then knows when we call the method on the dog or cat instance which method has to be resolved for which type so let's see Dynamic dispatch in Dynamic dispatch the specific methods to be called is determined at runtime and not at compile time like static dispatch it works by creating a reference or smart pointer to a trade object using reference din or boxed in when trade object is created the compiler will build a v-table for that trade a v table is a table that contains a pointer to the implementation of each method in the trades for the specific type of the object that the reference points to the compiler will do a lookup in a V table to determine which method should be called for which type that implements the given trade and this lookup will cause overhead but allows for more flexible code so exactly because of this lookup static dispatch is faster than Dynamic dispatch but on the other hand Dynamic dispatch allows for more flexibility so let's see how that might look like as you remember with box we are able to allocate a type to Heap memory meaning the type will then get allocated for example a i32 integer like 7 will get allocated to the Heap and we will then get back a pointer this is what box is for and we have seen examples of that now when we use box with a trait object it means that the type that implements the specific trade will get allocated in Heap memory get back a pointer to the location where the type that implements this specific trait was allocated and we will get back a v pointer right this is happening when dynamically dispatching so the V pointer points to the V table and in the v table there are certain Fields now there is also a field called Methods and this is where all the methods for the concrete type over here are located meaning at runtime the compiler will check this V table and see in the methods over here which methods are implemented for the concrete type that was allocated in Heap memory so let's see an example so we have over here a trade animal and we have two custom types cat and talk and we implement this method defined in the trade for both of our types right because when we Implement a trade for a custom type we have to implement all of its defined methods so over here we have seen we have a random animal function which takes a random number and based on this conditional it will either output a cad instance or a dog instance so then when we call this random animal function with the random number and for now just assume that this is really a random number that means the compiler first of all cannot determine the size of the type which gets returned and this is of type that implements the animal trade right cat and dog both implement the trade but we can't know the exact size at compile time meaning we put it behind a pointer in this case a box but a reference would also be possible and that means the size is now known right it's the size of a pointer basically U size 8 bytes in a 64-bit computer but we have another problem over here the return value will be a type that implements the animal trade but at compile time when we call the noise method over here the compiler doesn't know which of these types get returned so he so the compiler can't know which method he has to call is it for the implementation on cat or dog right so this is why we use Dynamic dispatch because that means this over here will get decided at run time so again the compiler will then look up in this B table over here using the V pointer which points to the V Table stores in heaped memory and and then it will check for the concrete type that got allocated on Heap memory this specific methods so for example again this is happening at runtime we return a cat then the compiler will see that the type over here which is concrete is cat so it will see in the v table the methods implemented for the concrete type of cat I hope this makes sense so this is why we need Dynamic dispatch here static dispatch doesn't work because again the output of this function is random so let's see box box is a smart pointer that allows to store data on the Heap rather than this stack and you can use box when you have a type whose size can't be known at compile time it returns a pointer to the data stored on the Heap so what's the difference between a reference and a box in terms of memory a box allocates the data on the Heap and owns it is also responsible for deallocating when the value goes out of scope and the reference only points to a value that is already in memory meaning the reference points to something that is already there while a box allocates and a and the reference also doesn't own anything it just points to something in terms of lifetimes a box can be passed across Scopes while the reference has limited lifetime and we will cover lifetimes very soon a box can be cloned and the reference cannot and the Box can also be used in pattern matching so we have left off over here and I hope by now you know that this over here is not possible we cannot return a type which implements the animal trade because these could vary in size and again over here we return a type that implements the animal trade now if this over here would be really a random number then we don't know exactly at compile time which type gets returned meaning we can't use here the noise method because the compiler can impossibly know at compile time of course which method he has to call is it this one or is it this one right and for that we will use Dynamic dispatch so again we can put this in a box and using here the pin keyword meaning we want to go with Dynamic dispatch here and that means we have to box these instances so now the return type over here is known at compile time meaning we can annotate it it would be a box which holds a type that implements the animal trade and this is using Dynamic dispatch meaning we then can call this noise method on this instance here because the compiler can check for this noise method at run time and not at compile time meaning it is determined which of these will get called at runtime when the program is running let's see and this is working trade bound the infiltrate syntax works for straightforward cases but is actually syntax sugar for a longer form which is called a trade bound when working with generic type parameters often must use trades as bounds to stipulate what functionality a type implements so over here we are calling the sum function with two arguments in this case 2i32 arguments and as you can see we could also call this with floats because we have here defined t t generic type parameter so as you notice X and Y Must Be of the same type T and the type will be T which gets returned and all we do over here is adding X Plus y now again to use this plus operator here we must Define that t over here is a type that implements the add trade because remember this would actually translate to this so it's critical that X implements the add trait right and we also have to Define over here the associated type output to be of type t let's see this is compiling let's actually call the function and print out the result so as you can see the output would be 10. and we can call it with float right so let's see exercise 8 fix the errors as you can see we have a pair struct which has two Fields holding values of type t meaning they are generic and then we have an implementation block for repair and we Define here an Associated function and this is not a method because it doesn't have self as parameter now over here we are instantiating a new pair and remember self over here uppercase s refers to the type of the implementation block meaning pair and then we return this instance so if I write pair here it would be the same thing then over here we have another implementation block for the type pair and as you can see we Define here that t over here should implement the debug and partial order so the type T over here the type of the two fields the type of the two Fields must Implement debug and partial order then over here we are defining a method compare display which takes a reference to an instance of pair and then checking over here is the value of the X field bigger than or equal to the value of the Y field if that is the case then self dot X will get printed out using debug notation that is why we need T to implement the debug trade otherwise we print out the value of the Y field so as you can see over here we have a custom type unit that holds an i32 integer now over here we are instantiating a pair struct and as you can see we are passing here as concrete values for X and Y Fields the unit type our custom type and over here it holds one and here three write an i32 the type annotation for that would be we would have a pair instance and T over here would be unit right because X and Y hold a type of unit so T over here will be unit now remember we have over here an Associated function so let's actually use that to instantiate a pair as you can see this is exactly the same thing as this but here we are using the new Associated function that got implemented on the pair type so I can delete that and again when we call on this instance the compare display method then we would have an error because as for now doesn't implement the debug and partial order trades so we have to derive them and by the way we also need the partial EQ trait and that's because we are here checking for equality all right so we knew so we need partial equality and because we have now derived these trades for this type it means unit implements debug partial order and partial EQ meaning we can over here compare for equality and ordering and we can print the field out using debug notation let's see and this is running so the largest member is y you will need two so we have here a pair with an X field of unit 1 and the Y field of unit 2. and then the compare display method will give us back you will need two right because the wife because the Y field holds a value that is bigger than the X field compare these two because this is a custom type is only possible because we have derived these trades alright see you in the next topic so before doing more exercises on trade objects I want to cover Associated types now an Associated type allows to specify a type that is associated with the trade when implementing the trade for a specific type we have to specify the concrete type it's basically a type placeholder that the trade methods can use in their signature and it's similar to generic types but they are more flexible because they allow a trade to have different Associated types for different implementing types let's see an example we have here the my trade and this has an Associated type called my type and as you can see we can then use this Associated type over here in the methods we are defining in the trade so we here for example Define that the return type should be of type my type meaning when we implement this trade over here for a concrete type like for example my struct then we have to define the concrete type as you can see when we implement the my trade for my struct we Define here that my type should be i32 and as you can see this method over here will then return an i32 type and this is defined over here and we can access the associated type using self uppercase double colon and the name of the associated type now again self here stands for the type we are implementing this Trade four so in this case my struct all right let's do some more exercises practicing trade objects as you can see we have here a trade bird that has one method then we have here two custom types duck and Swan so let's see for duck we have an implementation block and here we Define a method and this method takes a reference to the instance and prints out something and for Swan we also Implement a method now notice this has nothing to do with the trade these are methods that are defined directly on the struct right so here we Define a method fly which again takes a reference to the instance and prints out something now over here we are implementing the birth trait for duck and Swan meaning we have to implement the method defined by the trade so as you can see this Quark method we have to implement for both types then over here we want to instantiate a duck so we then can call on this stack instance the swim method so this will then get executed then over here we call the hatch a bird function with an argument of two and over here we call the hatch a bird function again with an argument of one so as you can see when we provide two we want over here duck when we provide one we want over here a swan and I see that because of the output when calling the quack method so let's implement this function we have seen that the function takes as an argument and interfer so let's call the argument species and let's define it as u8 now I will keep the return type open we will see that then so let's see when we get one let's actually match the species argument when we get back one we want to return a swan if we get here for the argument A2 we want to return a duck instance and because we have to handle all the cases I will just panic in case anything else has been passed so over here what would be the return type now think about it when calling this function over here we have to provide the return type now again the return type has to be of known fixed size at compile time now over here we don't know what we'll get actually return that compile time right especially if this argument over here would be something that is generated at runtime meaning we don't know the exact size because this one struct could be larger in size than the duck struct or otherwise so we have actually seen the solution we need to box the return type and V over here Define that it should be dynamically dispatched and that means we have to box these concrete values here and now we are good to go because also another problem would be that in a match every return type has to be of the same type now again over here the function could sometimes return a swan or sometimes a duck right depending on the argument and when we do it like that we actually return a type of box right so they have all the same return type now again because we are here using Dynamic dispatch we have to put it behind a kind of pointer and that means over here we will get a box holding a trade object and the trade object in this case is bird so that means and we have seen that the compiler will then allocate the concrete type for example over here a duck in the Heap memory and we'll create a v table with all the methods because when we call over here the Quark method on bird then at this exact point Dynamic dispatch happens because it is not known at compile time which concrete type got returned here this will be determined at runtime so at runtime the compiler will check the V table and see the right and will execute the right method call right in this case it would be for duck so the compiler will look this method up in the v table for the tag type which has been allocated in Heap memory and we got and notice something over here when we return a trade object the trade object actually only implements the methods from the trade in this case Bird right so we then can't call because we have here a trade object we can't call a method directly defined on the type so for example this swim method is not in the v table that the compiler creates I hope you see that because again we are returning a trade object meaning in the v table are only the methods from the trade so over here because we have created an instance on the concrete type we can call this three method but over here because we have a trade object we can only call methods from the trade let's see and as you can see this is compiling array with trade objects again we have the birth trait with the same method then we have duck and Swan and as you can see these are methods that are implemented directly on the concrete type then over here again implementing the birth trait for duck and so on we have seen that fill in the blank to make the code work so over here we want to have an array because we are here iterating over this Birds variable meaning we want to iterate over an array now let's first annotate we want over here an array of birds right so we want a type that implements the bird trade meaning in this case stock and Swan and this time I will use a reference instead of a box so we Define over here that we want an array holding a type that implements the birth trait and is dynamically dispatched all right and again because using Dynamic dispatch requires that we put it behind some kind of pointer either box or a reference now with this array let's say we want to have two elements one duck and one swan so let's provide here the instances and again we have to put that behind a pointer so the size is known so the size is known at compile time because remember the size of the elements in an array must be known at compile time in this case it would be you size the size of a pointer now then we want over here to iterate over these elements here and then as you can see for each element we are calling on it the Quark method now again because this over here is happening at runtime the Quark method will be determined at runtime which one to run here for stuck or for swan right let's see and this is compiling now overs and here we have the same thing bird.fly the method we have directly implemented on the type isn't available because in a trade object over here only the methods from the actual trade birth are in the v table other methods won't get into DV table and so the compiler cannot look it up at runtime referenced in and boxed in so we have here a trade draw which has one method and we implement the draw trade for u8 and f64. as you can see we can Implement our own custom trades for types in the standard Library and all this does over here is it will take a reference of the instance and and it will output the value now in the newer version of rust we don't have 2D reference self here this is happening automatically like that and for f64 the same thing now as you can see we have here an f64 and an u8 basically the types that implement this draw trade then over here we are calling the function draw with box and as you can see this function over here expects a trade object of the draw trade and this trait object should be boxed all right and then it is calling the draw method again the compiler will then look in the v table and will execute the appropriate method at runtime so let's pass the argument for the draw with box function and in this case we want to draw X so let's pass it X and of course this has to be boxed right like this now over here we want to draw Y and this time we are passing a reference meaning we over here expect a reference to a trade object that implements the draw trade all right and then we are calling the draw method again this time on y let's see and this is compiling let's see exercise four we have a trade full which has one method then we Implement Foo for u8 and string types and as you can see we can Implement our own trades on types that are defined in this standard Library so in this case u8 and string then over here in main we have a variable X holding a u8 and Y holding a string basically the types we are implementing for for right and let's see now the function called Static dispatch so we pass here X tu8 type and this should be statically dispatched so let's implement this function Implement below with generics so we need here a generic type parameter and we Define here a trade bound meaning we want T to implement the foo trade right because we want the argument to be of type t so it's important that we Define here the trade bound because we call this function only with arguments that implement the full trade and we are defining that in order to be able to call the method method on it basically this method defined here right because then we can pass types of u8 and string to this function and it can call the method because the full trade is implemented for this type any other type that doesn't implement the full trade won't be allowed to be passed to this function so this is static dispatch because the compiler at compile time will actually expand this code and create a function for the concrete types so in this case we are passing u8 to this function so the compiler will create a function like this right and then we'll call it and this is happening at compile time meaning the compiler will fill in the concrete types over here and then of course the compiler will know when we do a method call which method over here it should call right and static dispatch is usually a lot faster than Dynamic dispatch because you don't have to do a lookup it's everything in the binary itself but when you have to do a lookup you have to follow a pointer to Heap memory and then get the data from this memory location which is more expensive so let's see Dynamic dispatch in this case and we use Dynamic dispatch because sometimes it's just not possible to use static dispatch right when the size of the returned type it cannot be known at compile time then we use Dynamic dispatch and let's see how that looks like so over here we are passing a reference to Y so we are passing here a reference to a string now as an argument over here we want a reference right that's what we are passing to a trade object implementing the full trade all right and again because this is dynamic dispatch so this happens at runtime must be behind a pointer so this size is known at compile time because it pointer is always of size U size we have covered that and then we call the method on the argument right the difference is that over here this will then be determined at runtime which method over here to be called right because the compiler will then do a lookup in the v table at runtime and then determine for which type the method should be called let's see this is compiling object safe you can only make object safe trades into trade objects a trade is object safe if all the methods defined in the trade have the following properties the return type isn't self and there are no generic type parameters so let's see exercise 5 use at least two approaches to make it work don't add remove any code line so I think we will go first with static dispatch because it's easier so I will remove this box here and let's see we have a trade here defining one method now this defines that it takes a reference to a self meaning a reference to an instance of my trait and it returns self their self over here refers to the type for which the nitrate is implemented so in this case for example over here my trade is implemented for u32 meaning we implement this method over here and self over here would refer to u32 right we can write it either way and u32 and string Implement my trade all right so they both implement the F method then over here we have a function that takes a trade object now in this case you want to use static dispatch so we Define here a generic type right because we are calling it with new 32 and a string now we want to ensure that t this type implements my trade so we can be sure that it implements this F method so this is called trade bound and this makes sure that t implements my trade so we then can call the F method on the provided argument here which is of type t and that's basically it we have implemented the static dispatch for this function over here and what then will happen is when we call this function the roscompiler will take this generic function here and it will provide it with concrete types and it will provide it with concrete types so in this case u32 right we are passing it to 32. and we have we forgot to define the return type so the return type over here would be the return type of the F method now in this case it would be T type right so if we are calling it so if we are calling the F method on a new 32 then the return type will be u32 in if we call it on a string then the return type will be string so again we pass here T right because the type we are passing for example u32 will be the return type of this function u32 and that in case means that the return type of this function over here will be u32 right okay and then the same thing happens for a string so the compiler will actually put into these generic type parameters concrete types and that means when we are using static dispatch these method calls here are known at compile time so the compiler knows if you're passing a u32 type then it will call the F method from the eu32 implementation of my trade right and in case of string then the compiler will know that the F method can be found in the my trade implementation of string right so everything here is known at compile time and also the size is fixed and this is working now we want here to now use Dynamic dispatch so we solve it in two approaches so before we can even use this straight object over here we have to make the trade itself object safe and we do that when the return type isn't self and there are no generic type parameters so we cannot Define here self we have to provide it concrete types now in this case we want the F method to return a trade object meaning we return a pointer that points to the type that implements my trade all right and over here because we are taking here this function signature and implement it for each type we Implement in my trade for so as you can see the return type must implement the my trade and it has to be boxed so we have to box this over here turn over here A U 32 right which implements the my trade and over here a string which also implements my trade so as you can see we got rid of self because again at compile time the return type size has to be known that's why we are putting it behind a box now again the return type of this function would be the return type of Team method calls in this case it would be a boxed trait object right let's see if this is compiling and this is also compiling now I hope you see the difference here when we pass here a trade object basically a type in this case u32 that implements the my trade trade then this method call will be determined at runtime and that's why we have to put this behind pointers so the size remains fixed and known at compiled time and again use whenever possible static dispatch because it's faster and I guess easier to write but Dynamic dispatch has its advantages and you can use it for more flexibility in your code alright see you in the next topic so we have reached the topic of collection types and string is also a collection type meaning we are just repeating because we have covered strings but I think these exercises are good for some repetitions so string is a utf-8 encoded growable string it is the most common string type we used in Daily development it also has ownership over the string contents so let's see fill in the blanks and fix arrows don't use to string don't add remove any code line so as you can see over here we want a string type because we because we are here modifying the swing meaning it has to be growable so let's allocate this data over here on the heat so we have now a string type we can now let's see the poster method as you can see this push stir method is implemented on the string type and it takes as a type for its argument a string literal as you can see we are calling it on a string and providing it an argument of a string literal so we have to delete that because this would actually be a string right we want here a string literal like that and push is for pushing single characters so we want to push here an exclamation mark So s will then here match with this string literal and as you can see we are calling the move ownership function here meaning this function will then take ownership of s and that's not what we want because we are using here as again so what we can do is just clone it so the data passed to move ownership over here is actually cloned right it is copied on the Heap memory and that means we can still use S because s Remains the owner of this data all right then a string is stored as a vector of bytes but guaranteed to always be a valid utf-8 sequence string is Heap allocated growable and not null terminated a string slice is a slice basically a slice to u8 integers because as you can see this string type is a vector of u8 inter source that always points to a valid utf-8 sequence and can be used to view into a string just like a slice to T is a view into Vector T and we'll cover vectors after this topic so fill in the blanks we have here a string and we want to take a string slice from this string now the easiest method would be just to take a reference to s which means we take a reference to a string type and this can then be inferred by the compiler to a string slice and the second way would be to call the askster method on it this will essentially do exactly the same thing it will take the string s and we'll convert it to a string slice now over here we also want a string slice but we want to provide a specific offset we just want this data over here and that means we take a reference to s and provide it here an offset from zero we can omit the zero on here until index 0 1 2 3 4 until index four now we would provide here 5 because 5 is excluded meaning we go from index 0 to 4. and over here as you can see we want to modify the string holding then hello world because we are pushing to it this exclamation mark as you can see we want it hello world and with an exclamation mark so what we can do is taking s over here s mutable reference so this would then hold a mutual reference to a string let's see and this is compiling now notice over here we could also take ownership of s meaning slice 3 will hold a string because we are not using S after this point so slice 3 can be the owner of s but in this case we have to make the variable mutable and this is also working question how many Heap allocations are happening here so we have here a string and when we create a string type then the data that is provided is allocated in Heap memory so this would be the first allocation then over here we are taking from this string over here a reference meaning a string slice and remember a string size is just a view into some data so we are just going to this location and checking what's in there so no allocation is happening and over here we are taking this string slice and convert it to a string meaning a heap allocation is happening over here right because we are taking this string slice holding this and then we allocate this into the Heap once more being because we are calling this two string method let's see indexing so we have a string here and we want to index into the first element so slice one should then hold h so we want a string slice so we take a reference to S and provided the offset from 0 to 1. 1 is excluded meaning we just take the first character and that is because H only takes one byte in utf-8 format so ASCII characters in strings usually take one byte while this Unicode characters over here take three bytes so when we take a string slice here we want to access this character now for that we have to provide the right offset so we go from 0 1 2 3 4 5 6 7. starting at index seven and as you can see these Unicode characters hold three bytes meaning we must provide here an offset of 10. 10 is excluded meaning we go from 7 8 9. three bytes iterate through all Source in s so to iterate over characters in a string type we use Stitch Source method but that will actually just return the Char we are iterating over but as you can see we want here as return type a tuple which holds the index and the actual character so we can use here to enumerate method and enumerate will return a tuple with the index and the specific character so in the first iteration you would have zero and H right the index and the character so here we are checking if the index over here is 7 then the variable C should hold this character right let's see and this is compiling now there is a crate called utf-8 slice you can import it into your project and this then allows you to slice into a string not using bytes as offsets but just indexing into the individual characters so over here we provide an offset from four to five meaning we want the character at index 4. so we don't have to worry about the byte size of this symbol then fill in the blanks we have here a string now we have seen that a string is actually a vector holding u8 intervals and as you can see we have here some bytes in a vector so this Vector holds u8 inters right and that means these are bytes that can be converted to ASCII characters so in this case these over here will translate to hello all right if you look up this code over here in the ASCII table then we will find that this is represented for the character age and this e l l o so we want to turn a bytes Vector like this into a string so we want to be string here so let's see this method here so in the documentation you can see that the string type implements a method from utf-8 and this just takes a vector of bytes and then we can call this Associated function on the string type and provide it as an argument a vector of bytes and this will then convert it to a string so we can try it out so first of all we want to actually mutate the string here to hold the same data as this Vector holds namely hello so we can do that by using poster and we push hello meaning this string then holds hello and then we can take this string type and call from utf-8 method providing it this vector and this should then convert this Vector of bytes into an actual string meaning s and S1 will then hold the same value namely a string hello and of course this actually and this returns a result so we have to unwrap it and as you can see this is compiling so in fact a string is just a vector of bytes right and we'll cover vectors very soon and you will then see how this is actually stored in memory so a string is made up of three components a pointer to some bytes a length and a capacity the pointer points to an internal buffer string uses to store its data the length is the number of bytes currently stored in the buffer always stored on the Heap and the capacity is the size of the buffer in bytes as such the length will always be less than or equal to the capacity we have seen that the string type is a three bird object holding a pointer to the actual data stored in Heap memory the length which shows the length of the string in this case 5 and the capacity and the capacity as you can see here is the total amount of memory received from the allocator so when we allocate a new string type on the Heap the allocator we will find an empty spot and then we'll return a pointer to that location number six if a string has enough capacity adding elements to it will not really allocate so when we run this program over here then we see the capacity of string s now over here we are initializing an empty string meaning we have an empty string so nothing gets allocated to Heap memory we still have a string object but with a length of zero and a capacity of zero then we print it out as you can see Zero over here we are iterating two times modifying this string so in the first iteration s will then hold hello so it would have a length of five but when we print out the capacity it will show 8. so we have a string object that points to this data with a length of 5 and a capacity of 8. now this is the default behavior when we don't specify a capacity ourself so when the length exceeds the capacity then rust has to reallocate the data meaning it has to find a new spot in Heap memory that is big enough to hold the data and then we'll return a pointer to the new location so in this case because we have modified the string the length 5 exceeds the capacity zero so it has to reallocate because remember in Heap memory the data is allocated as contiguous block of bytes all right and the default behavior is that the capacity will always double so it starts from 1 then 2 4 8 16 and so on and that's the reason we have here a capacity of 8 even though we only need five right this is a length this is a string of length five but because of the default behavior of the allocator we will get back a capacity of 8. and then in the second iteration we again push hello right so we would have a length of 10 but as you can see the capacity doubles so 10 exceeds the length of 8 meaning the allocator will then find a new spot in hit memory to allocate this string over here of length 10. and it will then Reserve 16 bytes of memory in Heap memory and so on if we then exceed 16 bytes then it will allocate to a location which holds 32 right it always doubles and this is when you let rust allocate so this is the default Behavior but as you can see this might be expensive so if you start with a capacity of zero and then you modify the string multiple times it means rust has to reallocate your daytime and that means it could potentially be expensive when you mutate this over here multiple times and a lot of reallocations are happening so there is a function called with capacity and then we can Define over here that we want an empty string meaning the string length is zero but we want a capacity of 25 meaning s is allocated in Heap memory and there is enough space reserved for this data to hold 25 elements right so when we do that again then as you can see no reallocation has happened we have only allocated the string one time in heat memory with a capacity of 25. now because the length doesn't exceed the capacity in any of this modification of the string it means that there is no need to re-allocate the data right so when we print out swing s over here in the end then as you can see this is the string that in the end hello hello so we have here 10 elements meaning there is no need for reallocation because a capacity of 25 can hold the string of this size and this can make your program a lot faster so let's see vectors a vector is like an array but dynamically sized meaning they can grow and Shrink it's allocated on the Heap as contiguous block of memory all elements in a vector have to have the same type and there is a special macro vac which you can use to initialize a new vector so all this time we've been dealing with an imposter so in actuality this string type is just a vector of utf-8 bytes that gets allocated in Heap memory so as you can see when we have the S1 variable over here then again S1 won't hold the actual data but a pointer to the data allocated in Heap memory alongside a length and a capacity meaning S1 is a three word object of size 24 bytes now as you can see this would be a vector allocated in Heap memory that holds the utf-8 bytes over here of course in memory this would be in binary but as you can see this then holds the character code for this character inside the string so let's do some exercises vectors or resizable arrays like slices their size is not known at compiled time but they can grow or Shrink at any time so over here we have a normal array holding three u8 elements now we can convert this array to a vector using the from Associated function and then passing it the array meaning we will then hold a vector of u8 elements and here you can see the difference between an array and a vector we don't provide here the length of the vector because the vector can actually grow or Shrink meaning it's dynamic in its size now the isvac function basically just checks if the provided argument is a vector then as you can see we are shadowing V and we are here initializing it with a vector of these elements so as you can see you can use the VAC macro to quickly create a new Vector like that so this will then hold a vector of u8 elements and by the way you can either use square brackets or you can use parentheses it's both exactly the same now as you can see here we are initializing V or basically shadowing it and then we are passing it to is back meaning ownership would be transferred but we are using V here again so let's clone this and over here as you can see we are initializing V1 with Divac macro now we are passing to the back macro the array Mac macro over here won't convert it directly to a vector like this from Associated function did instead we would here have a vector holding this array all right I hope you can see the difference here we have a vector of u8 elements over here we have a vector holding an array of three u8 elements so when we compare over here V and V1 they won't actually be the same right and again let's do clone here just so we can then reuse B1 now what we can do over here to fix that is using the VAC new like that and back new basically will then create a new empty Vector meaning it would be an object with length of 0 and capacity of zero now over here we want to iterate over the elements in v let's take a reference here because we are using V again and we don't want this for Loop to take ownership now all we want to do is pushing all the elements from we into V1 and because we are here iterating over references of elements we have to de-reference it and by the way we will take a look at that in much greater detail so what we also have to do is making this V1 variable mutable because when pushing to it it has to be mutable alright let's see and this is compiling so in the end V1 will hold a vector of U 8 elements meaning V and V1 are exactly the same but remember when you have an array use the from Associated function and not the VAC macro because it won't convert it directly to a vector instead it will create a vector holding an array Evac can be extended with extend method so over here we have a vector let's say these are i-32 values and as you can see we can pop meaning removing the last element so over here the vector will then hold 1 and 2 because the last element got popped then we are pushing to it 3. meaning we have a vector holding one two three now over here again we are initializing an empty vector now as you can see we want V1 and V2 to be exactly the same so what we can do is using the extent method and providing it the V1 vector meaning the elements foreign and we have to pass here a reference meaning the elements then from V1 will be put into V2 so again V2 will then be a vector of I 32 elements and they hold exactly the same data right it's basically copying the contents of a vector into another vector turn X into back fill in the blanks so over here we have an array and this array holds i32 types and the size of the array would be 3. then as we've seen we can use the from Associated function so we create a vector from this array meaning V1 will then hold a vector of i32 types or what we also could do is calling the into method and if a type implements from then it will Implement into and we will take a look at that so we then take this array over here and convert it into a vector of i32 values right so that means V1 and V2 will be exactly the same now converting a string into a vector so the from trade over here is actually implemented for the vector type so over here we have a string and remember basically a string is a vector of u8 elements and then again we can call here into right converting s into a vector of u8 elements and then over here we have again a string and we call the method in two bytes meaning V2 holds a vector of utf-8 bytes represented as u8 and this is actually exactly the same so meaning V1 and V2 will be identical then over here we have a string literally and we want to create a vector from this variable meaning we again use the from Associated function and provide it as an argument over here meaning B3 will then hold a vector of u8 types right so V2 over here and V3 are identical iterators can be collected in two vectors so when we have here an array of 10 elements of value 0 and we put it into an iterator we can then collect it into a vector all right and we will see these methods over here in Twitter and collect in much greater detail meaning V4 will then hold a vector and of 10 times 0. like that all right let's see and this is compiling fix the error and implement the code so let's see over here we have an array and we convert this array into a vector using the from Associated function we have seen so this will then hold a vector of I 32 types all right now then we are iterating here five times and just print out each element at the specific index here we have to implement some code and actually V in the end should hold a vector with these elements so as you can see we have we are starting with a vector holding one two three and we end with a vector 2 3 4 5 6. now what we want to achieve here is we are adding one to each element then we would have 2 3 and 4. now if we exceed the boundary over here then we want actually to add the new element over here now let's see first in the for Loop here this would actually panic because we are trying to access indexes that are out of bounds so over here we are iterating from 0 to 4 right 5 excluded but we have only three elements meaning meaning indexes 0 to 2. now we can here you stick get method and we have seen this get method before and and this is generally safer because it will return an option in this case option i32 because we are dealing with i32 types here and what we then can do over here we want to actually manipulate this Vector so let's first access the elements and as you can see again I use the get method because we want to actually see if the index is out of bounds or not so in case it's not out of bounds we would return a sum right the get method will return a sum and the actual value at that specific index so if we found an element at the specific index over here then we want to access the element and just add to it one right we are then taking the element over here let's see for example in the first iteration I will be zero then we pass 0 to this get method meaning we will get back some 1 right so we'll get back some 1 meaning e over here will then hold a value of 1. then we access this array at index 0 right and what we want then to do is taking 1 E over here and add to it one meaning then the first element in this Vector has been modified to then hold two now in the second iteration to do it would be exactly the same thing so I would be 1 we get over here the element at index one and E will then hold two and then over here V at index 1 should then hold two plus one right and we assign it meaning in the modified vector the second element will then hold three now we do that again and after we are out of boundaries meaning we return a non here and then we want to push to the vector the index plus two right so for example in the third iteration I would be three right this would be out of bounds there is no index 3 here so we get back a non and then over here we push to V the index in this case 3 plus 2 meaning we then get back 5 right and let's see if this is actually working all right and let's remove this type annotation because this would be of Type U size because of the get method now as you can see when we print out here the output of the get method we would get someone sum two and some three and then two times none because at iteration 3 and 4 we would get back none because there is no element in this index and we are succeeding here because again we have three times sum here and when we have a sum we just add 1 to the value meaning we then have 2 3 and 4. and then we will get back two times none meaning we take the index and increment it by two pushing it to the vector so we get five and six slicing a vac can be mutable on the other hand slices are read only objects to get a slice use reference in Rust it's more common to pass slices as arguments rather than vectors when you just want to provide read access the same goes for string and string slices so over here we have a vector let's say i32 then we are taking a slice from V over here meaning this would then be a slice of i32 elements right and this would be the offset meaning we would take from the first element to the last element out of bounds will cause a panic you must use the length here so as you can see again we are taking a string slice foreign but we are here out of bounds right because this would iterate from 0 to 3 for excluded and we have here only zero one two indexes so what we can do is using B dot length meaning we just iterate meaning we provide an offset from zero the first element to V length where the length here is excluded meaning vlanth minus one all right slices or read only node slice and reference to back are different so as you can see we take here immutable reference to a vector and then we can push to this Vector over here the value 4. now we have seen that slices are immutable they are just a view into some data so we can't use a slice to mutate the vector right so over here we want to have a slice of i32 elements right like that and we can remove the mute keyword over here because again we can't modify slices so we have pushed to it for meaning when we take the slice we want to take also this last pushed element right and this is compiling capacity the capacity of a vector is the amount of space allocated for any future elements that will be added onto the vector this is not to be confused with the length of a vector which specifies the number of actual elements within the vector if a vector's length exceeds its capacity its capacity will automatically be increased but its elements will have to be reallocated for example a vector with with capacity 10 and length 0 would be an empty Vector with space for 10 more elements pushing 10 or fewer elements onto the vector will not change its capacity or cause reallocation to occur however however if the vector's length is increased to 11 it will have to reallocate which can be slow for this reason it is recommended to use the width capacity function whenever possible to specify how big the vector is expected to get and this we have already covered with strings but now let's see with vectors so over here as you can see we are initializing an empty Vector but with a capacity of 10 so this object here would hold length of 0 and capacity of 10 meaning the actual elements inside Vector are zero there are no elements but in Heap memory there were 10 locations allocated for this data right the vector contains no items even though it has a capacity for more so when we call the length method over here we would get back zero right because back doesn't hold any values but it has a capacity of 10 because we have defined that over here these are all done without reallocating so we are then pushing 10 times to the vector right now this means back here holds enough space that this is happening without re-allocating the vector right so after pushing to the vector T Vector with would be of length 10. and capacity 10. right so the length must never exceed the capacity otherwise a re-allocation is happening as we can see here but this may make the vector reallocate so we are pushing another element to the vector meaning the length will be 11. this would exceed the capacity meaning then this whole Vector will be reallocated in Heap memory so when we have a length of 11 then the capacity will be at least 11 but it can be more because we have seen that the default behavior when reallocating is that the capacity doubles feeling an appropriate value to make the four done without reallocating so as you can see we have here a vector we want to initialize with a certain capacity then we are pushing 100 times to this vector meaning the length here will be 100 so what we want to do is we want here to initialize a vector with a capacity of 100 meaning we can push 100 times without this Vector to be reallocated in Heap memory store distinct types in Vector the elements in a vector must be the same type for example the code below will cause an error so we have here integers and a float this would cause an error because again all of them have to be of the same type but we can use enums or trade objects to store distinct types so over here we again see the IP address enum we have seen before in the examples now fill in the blank we want over here a vector that holds IP addresses meaning at index 0 we want this variant here and at index 1 we want this variant now we can use the back Macro for that and then over here I just copy this variant and this over here right we would then have over here a vector holding types of IP address and because this is a custom type we have to actually Implement partial EQ here so I hope you get the concept we have here a vector holding types of IP address right then over here we have a trade IP address that defines one method display and then we have two Tuple structs V4 and V6 we implement the IP address trade for v2 struct and V6 tract meaning we have to implement the specific method and all it does it then basically prints out the value that is stored inside destruct so fill in the blank as you can see we won't hear a vector holding trade objects remember a trade object is boxed or put behind a reference so over here we have a vector holding boxes right and the Box holds a trade object meaning again this is using Dynamic dispatch and we won't hear a type that implements the IP address straight right in this case B4 and V6 both are types that Implement IP address and as you can see even though we are holding here different types we can use it using here trade objects to store it in the same vector typo so and then we are iterating over the vector and for each element in here we just call the display method that has to be implemented because we only have types in here that implement the IP address trade and this is exactly where Dynamic dispatch happens and we have covered that let's take a look at hash Maps so a hash map is a data structure to store key value pairs it's allocated on the Heap as it is dynamically sized meaning it can grow and Shrink it allows for efficient lookup insertion and deletion of data so when talking about time complexity this means accessing and modifying elements or entries as they are called in hashmaps would be in the best case at constant time meaning Big O of 1 while in the worst case if you have a lot of collisions then it would be still linear time which is still pretty fast which means Big O of n each key is hashed to a unique index in the underlying array so there are vectors stores values by an integer index hashmap store value spy key it is a hashmap implemented with quadratic probing and simd lookup by default a hashmap uses a hashing algorithm selected to provide resistance against hash those attacks so over here we find some information about the used algorithm for the hash Maps but let's dive into the exercises so the first thing is we actually have to use here a namespace so we are basically importing from the collections module from this standard Library the hashmap type and when using vectors or strings these are all in the Prelude meaning we don't have to import it but a hash map you will have to basically import so over here we are initializing an empty hash map then we insert some entries and again a hash map holds entries of keys and values now important here is that all the keys must be of the same type and all of the values must be of the same type so this over here would cause an error and let's go with integers like that and we annotate the hash map like this where key stands for key and v stands for value so over here we use string literals as keys and i32 integers as values then as you can see we can access the entries in a hash map by using again the get method which returns an option we have seen that before when dealing with vectors so we provide the get method the name of the key all right and then we will get back the appropriate value in this case 98 because we are accessing the entry sun phase right so we would get back here a sum an option i32 right this would be the return value some 98 and this would be the type option i32 so we can check if the hashmap contains a certain key so for example if the hash map contains the key Daniel this would evaluate to true meaning the code block here will be executed and we can also index into a hashmap using bracket notation like that so again this would then hold the value of the entry Daniel it would be 95 right this would be an i32 again notice the difference between indexing like that or indexing using using get method we will get back the value here directly while here we get back an option so get is generally safer and then over here we are removing Daniel from the hash map right so we can use the remove method and provide it the key the entry we want to delete meaning after deleting Daniel over here the length of the hash map would be 3 right and we can also iterate over a hash map so we provide it here the variable holding the hash map and then the return would be a tuple holding the key and the value and then we just print out the key and the value let's see all right and we would get back here an option holding a reference to i32 all right and over here too we would get back a reference this is what the get method will actually return so as you can see we are printing out the hash map and this would be the key for example Ashley and this would be the value as you can see now notice that the order here is different from what is over there and that's because a hashmap is unordered so the order won't be retained all right and of course we have only three elements here because we have removed the entry of Daniel then over here we have an array holding tuples which holds a string literal and an integer right so we have an array of three elements and each element is of type Tuple and inside the Tuple is a string literal and an i32 then over here we are initializing a new empty hash map then we iterate over here through the elements of this team's array so for each element here we insert into this empty hash map here the name over here the string and the value all right Implement team map 2 in two ways tips one of the approaches is to use collect method so we can also implement a hash map from an array holding tuples of keys and values by using the hash map from function so we provide it here teams and this will then directly convert this array holding tuples of keys and value pairs to a hash map meaning we then have a hash map holding string slice keys and i32 values all right and over here we would have the same thing and then when we compare teams map 1 and teams map 2 they should be equal let's see this is compiling now the second approach would be taking this teams array here and we want to put it into an iterator and again we will cover that and then we call collect now when we call the collect method it will basically collect the elements in this iterator into the type we have annotated here again this would compile because we have in teams map 2 the same keys and values as we have in teams map one and as you can see we don't need to write a full-blown for Loop we can just use some simple methods let's see number three fill in the blanks so type inference lets us omit an explicit type signature which would be hashmap string slice and u8 in this example so again we don't have to annotate the types I do it just to make things more clear but we can leave it at that insert a key only if it doesn't already exist so here we are accessing player stats this empty hashmap and we call the entry now the entry checks if an entry exists with this specific key and then we can call over here the or insert method meaning if this entry doesn't exist then we insert this key with this value meaning when we then access player stats with a key of Health we would should get back 100 right because this has been inserted over here insert a key using a function that provides a new value only if it doesn't already exist so again we are trying to access player stats and look for an entry Health now in this case this exists right because we have inserted it at that point now we can also use here the method or insert with the difference to our insert is that we don't provide a concrete value but a function pointer meaning this function will then get executed so this function only returns 42 so that means if the entry Health doesn't exist we insert this key with the value of 42. now again Health exists meaning when we Access Health we will still get 100 right because this only executes if N3 isn't found ensures a value is in the entry by inserting the default if empty and returns immutable reference to the value in the entry so we are here checking for the entry health and if it doesn't exist we insert 50 right so in this case Health already exists meaning we would get back immutable reference to the existing value 100 right so this would be immutable reference to 100. and because it is mutable we can then modify the entry manually so we can hear decrement from Health which holds 100 we decrement 50 meaning the health will then hold 50 right this health variable holds 50. let's see this would be u8 in this case and this is compiling so as you can see when we call entry it will give us back a mutable reference to the value if it already exists to the existing value if it doesn't exist it will here create an entry because this is what the or insert method does and will return immutable reference to the created value and then we can modify this mutable reference right requirements of hashmap key any type that implements C EQ and hash trades can be a key in a hashmap this includes pool intu in string and string slices note that F32 and f64 do not Implement hash likely because floating Point Precision errors would make using them as hashmap Keys horribly error prone all collection classes Implement EQ and hash if their contained type also respectively implements EQ and hash for example a vac holding a type T will Implement hash if T implements hash so if the elements in a collection for example a vector or a tuple or an array implement the hash trade then the collection itself will Implement hash let's see fix the errors tips derive is usually a good way to implement some common use traits as you can see we have here a struct viking with two fields of type string and here we have an associate function for Viking just creating a new Viking instance from the arguments we provide and as you can see use a hashmap to store the Vikings Health points so we are creating here a hash map from an array holding tuples of keys and values now notice we have here a hash map and the type of the keys would be the custom type y King all right the value would be i32 use derived implementation to print the status of the Vikings so here we are taking this created hash map as a reference and we would get back a tuple holding the key and the value and then we just print out the key and the value here so we have seen that in order a type can be used as a key in a hash map it has to implement EQ and hash so let's Implement that let's also use the partially queue over here and debug so we can print out the key over here in debug notation let's see and of course we have to implement the hash trade and as you can see it will print out each element in the hash map we have created so we have now a hash map using as key our custom type y King as you can see this would be the key this would be the value of the hashmap capacity like vectors hash maps are growable but hashmaps can also shrink themselves when they have excess space you can create a hash map with a certain capacity using hashmap with capacity or use hashmap new to get a hashmap with a default initial capacity this is recommended so like we did with strings and vectors we can Define when initializing an empty hashmap the capacity right ensuring that when we know we will push a lot of entries that there are no reallocations because remember we allocations are slower so then we are here inserting two entries and as you can see this would be an entry of key i32 and value i32 so the key entity value can be of the same type and over here we insert again another entry indeed the capacity of hashmap is not 100 so we can't compare the equality here so even though we have here defined that the capacity should be 100 it could actually be more than 100 and this is just in case there are collisions there are some internal stuff going on so the RAS compiler will actually allocate more capacity than 100 we defined here shrinks the capacity of the map with a lower limit so when we call map over here our hash map holding two entries and then we can shrink the capacity to 50 right so again the capacity won't probably be exactly 50 then but around 50 so we can shrink the hash map and shrinks the capacity of the map as much as possible so because over here we have only a hash map of two entries 50. spaces in Heap memory would actually be more than we need so we can use this shrink to fit method so that the capacity will shrink around the amount of elements it actually holds again it won't probably be 2 exactly but around this number ownership for types that implement the copy trade like i32t values are copied into hashmap for owned values like string the values will be moved and hashmap will be the owner of those values fix the errors with least changes don't remove any code line so over here we have initialized a variable with a type of i32 and an empty hash map right then we insert here and N3 into M1 and using the same exact value for the key and the value meaning we have here a hash map with keys of type i32 and values of type i32 over here we have a string and we initialize here an empty hash map then over here we are inserting a new entry using V2 this string here as the key and V1 over here as value so we would have here a hash map using keys of type string and value of type i32 and because this is an owned type meaning it doesn't Implement copy this will then actually be moved into the hash map so we can't use V2 here but what we could do to fix that is we could use a string slice of this string right we just provide a reference meaning then the hash map will have type for the keys of string slices right and that means we can then again use V2 and again because V1 is of type i32 it means it implements the copy trade meaning even if we pass it as a key in the hash map or as a value it is still usable but when you are dealing with types that don't Implement copy the ownership will get moved meaning hashmap will then be the owner of the type alright and there are some third-party hash lips but I won't cover that in this beginners course so we'll see you in the next topic let's now look at type coercion and we have already seen the S keyword and this is mostly used for converting integers so type conversion also called type casting is coercing a primitive type that can be performed by as keyword as conversions can be Chained and when casting to an unsight type T for example then T Max Plus 1 is added or subtracted until the value fits into the new type and using unsafe methods can lead to undefined Behavior so let's see an example as you can see we have here the largest unsigned 8-bit integer 255. and this would be the binary representation this means that u8 right this data type for unsigned 8-bit integers Max meaning 255 plus 1 will be 256 so for example if we Typecast 1000 s u 8 then let's see what the compiler will do it will subtract 256 this value over here from 1000 and then it will check seven hours 744 is still bigger than the largest value it can represent meaning it will do the same thing subtracting 256 will leave us with 488 still bigger than this value so again the same thing it will subtract 256 and this will leave us with 232 meaning this will then be the returned value when we cast 1000 s u 8 because remember an 8-bit unsigned integer can't represent 1000 it's out of bounds now when we Typecast -1 as u8 then remember an unsight integer cannot represent negative numbers meaning what it will then do it will basically rotate so 0 would be the smallest possible number an 8-bit unsigned integer can represent and then -1 will just basically rotate it will go back to the biggest one meaning this type casting over here will result in 255. so convert by S rust provides no implicit type conversion coercion between primitive types but explicit type conversions can be performed can be performed using the as keyword fix the errors and fill in the blank don't remove any code so over here we have an F 32 float right then we Typecast this float into an u8 meaning over here the fractional part will get removed so the value would be 97. and of course this will then hold a u8 now we want to Typecast this decimal into HR now this is not possible directly but we have seen that we can chain as statements so we can convert this first to an u8 meaning we will get 97 like we did here and then Typecast it to a character so C1 will then hold a character and by the way 97 would be the ASCII code for a and over here as you can see we can Typecast this integer holding an u8 type as Char directly so C2 would also hold HR now as you can see we are here comparing the integer holding 97 with this type casting now this type casting over here converts the B character to its u8 representation now B would be represented as 98. so what we can do over here is adding one 297 because remember 97 would be a meaning when we add 1 to 8 we will get back B right so both of them will hold 98. by default overflow will cause compile errors but we can add a global annotation to suppress these errors so as you can see the largest possible number u8 can represent would be 255 meaning this would cause an error because again 1000 is larger than the largest possible number a weight can represent meaning we have here an overflow error now we can actually allow overflowing by using this attribute so distance won't complain right and then when we print out B as you can see we would get back 232 because we have seen over here that this would be the result when the compiler performs this operation so we allow here overflowing literals right if we didn't if we comment this out then the compiler will complain because this is actually not possible when casting any value to an unsight type T T Max plus one is added or subtracted until the value fits into the new type that's what we have seen so in this case 1000 as u16 would return 1000 right because a u16 can represent a number of 1000 but over here we would have an overflow meaning this would return 232 like we have seen over here for positive numbers this is the same as the modulus so this would actually be the same as uh doing a thousand modulus 256 remember u8 Max plus one meaning 256. now we have also seen in the example when we have -1 and casting that to an unsigned integer it will then basically just rotate so this will return 255. since rust 1.45 the S keyword performs a saturating cast when casting from float to int if the floating point value exceeds the upper bound or is less than the lower round the The Returned value will be equal to the bound crossed so in this case we have a float here casting it to u8 meaning a saturating cost will be performed so it will just return the largest possible number u8 can represent which would be 255. now in case we have here A minus 100 as floating point and convert it to u8 this just would return 0. it's a possible number u8 can represent this Behavior incurs a small runtime cost and can be avoided with unsafe methods however the results might be overflow and return unsound values use these methods wisely we can use here in an unsafe block the method to int unchecked so the compiler here won't actually check what's going on so as you can see we are casting 300 floating Point 2 and U int to a u8 so as you can see we are here casting 300.0 floating point to and u8 integer and this won't be checked by the compiler that's because it's in an unsafe block now this would return 44 right because 300 minus u8 Max plus one would be 300 minus 256 meaning we would be left with 44. and minus 100 Su 8 as you can see we are typecasting this floating point 2 and u8 integer and again this is unchecked so again what the compiler will do here it will take u8 Max plus one meaning 256 and it subtracts 100 meaning we would get 156. and not a number would be converted to zero okay thank you and of course here we have to use again the attribute allow overflowing literals just so this will actually compile because usually this won't compile and as you can see this would be the output now over here we are dealing with raw pointers and we won't cover that in this beginner's course because this is more of an advanced topic so see you in the next topic so we have seen type conversions for integers and types from the standard library but what if we want to convert types that we defined basically custom types and rust has for this T from entity into trade so let's see from and into trades are used for type conversions between different types without requiring explicit costs it's part of the standard library and can be implemented for custom types implementing the from trade for a type will give us the intu implementation for the given type for free meaning when we Implement from we implement it into automatically so always implement the from trade and not the into trade let's see here an example we have a custom type number and then over here we are implementing the from trade right so we want to convert from an i32 to our custom type number right and the from trade implements a method from so we take here as an argument an i32 and we will then create a new instance of the number type with the provided value and then return it and that means when we use here the from Associated function on our custom type number and provide it in i32 then this can directly be converted to our custom type right and we can do it in two ways from as we've seen because we have implemented it and I've told you that when we and I've told you that when you implement it from trade T into trade is automatically implemented meaning we can also call the into method on the type we want to convert to but when you use into a type annotation is needed so the compiler knows into what type you want to convert this type right so we convert here a type of i32 into a number and this would be the implementation so the from trade allows for a type to Define how to create itself from another type hence providing a very simple mechanism for converting between several types deformant into trades are inherently linked and this is actually part of its implementation it means if we it means if we write something like this we Implement here so we Implement here the from trade for the custom Type U then we can use U from T right we can then create a u-type from the type T we have implemented the front trade on or we could also use into so we convert T into U the input rate is simply the reciprocal of the from trade that is if you have implemented the from trade for your type then the input rate will be automatically implemented for the same type using the into trade will typically require to type annotations as the compiler is unable to determine this most of the time so for example we can easily convert a string slice into a string as you can see we have over here a string slice and then we can call string and the from Associated function providing it this string literally meaning this will then be converted to a string and we could also use the two string method again this and this is exactly the same and we could also then use the into because remember when we Implement from we get into for free so we can so we convert this my string string literal into a string and again type annotations are needed because the sender library has already implemented this for us input from string slice for string so we can easily convert a string to a string slice and vice versa some implementations of form can be found here so as you can see we have here input from Bool for i32 meaning we can convert Boolean into an i32 so when we have here a Boolean value and call into it will convert this Boolean value into an i32 integer in this case this would be zero zero for false and one for true and again when we can use the into method we can use the from Associated function so we call the associated function on the type i32 and provide it here the Boolean value right so this will then actually be exactly the same fix the error in two ways first Implement from Char 4 maybe you should check the docs mentioned above to find the answer second a keyword from the last chapter so let's see the docs so we want here to convert a character so let's see over here as you can see you can see all the types that implemented from trade so we want to go from character and as you can see we can convert a character to either u32 u64 or u128 as well as string so I 32 is not supported and as you notice these are only unsigned types because it's logical a character can't be negative right it can't hold a negative number so we only have unsigned intervals here so we can so we can then in fact convert HR into a u32 let's say so in this case Char implements so in this case we use here u32 right we can convert from HR to Au 32. meaning when we change this over here this should actually work so we provide here a character called the into method and then we should have a new 32. fix the error in two ways so we can't use the as keyword here this is not implemented but what we can do is let's first use the from method so we can create a string or basically converting a character into a string because we have seen in the documentation this is actually implemented and the second way would be to call a and then into right like we did over here Implement from for custom types from is now included in standard Prelude so there is no need to introduce it into the current scope so we don't have to uh so we don't have to import the from trade it's actually in the Prelude meaning we can use it without importing anything so over here we have a custom type number with one field now we want to implement the from trade for our custom number meaning we should be able to convert from an i32 into a number so when we call over here number from right then we should get over here number this would then create an instance of the number struct and the value field here will hold a value of 30 right so and the same thing is when we would call 30 like that and call into right this should then convert the i32 integer into the number type automatically and remember we only have to implement the from trade because then we can use from and into both of them so let's see so this over here is the from trade and let's take this function signature over here and copy it like that now in this case self refers to number right and what we want to do is create here a new instance and we just take the value in this case the i32 right and we provide it as a value for the value field and again because the name of this argument matches the name of the field we can use it like that and this is sufficient so let's see and this is compiling so as you can see then it's really easy to convert one type to a custom type we have created when performing error handling it is often useful to implement from trade for our own error type then we can use question mark to automatically convert the underlying error type to our own type now we didn't cover that this will be the next topic but let's see the exercise we have here an enum CLI error now the two variants here IO error and parse Arrow hold Arrow types from the standard Library okay and these are actually the error types that get returned in case of an error when using these specific methods that are implemented in this standard Library so as you can see we want here to implement the from trade for different error kinds from this standard library and we want to convert that to our custom error type CLI error so let's see over here we have the function open and pause file it takes a file name as argument and it returns a result and we will cover results so a result basically says in case of success it will return a result with i32 and otherwise CLI error so then we call the read to string function and as you can see this is defined in the fs module FS is used for file manipulation and we provide to this function over here the file name now we pass a reference because this is defined in the documentation of this function we are here using the question mark operator meaning in case of success the result type that will get returned will be unwrapped and contents will hold a string in case of an error it will return the CLI error meaning the rest of this code won't get executed and we just get acli error instead now the error type returned from this function would be IO error so in order this error to be converted to a CLI error we have to implement for this error type the from trade right but let's see what's happening here we then take the contents meaning this string over here that gets returned in case this function call is successful and we then hold over here the contents of the provided file then we trim and then we pause now pause could potentially fail and parsing is basically converting a string to an integer value right so in case of success we will get out an i32 in case of error we'll get back a num pulse and error this is defined in the num module again we will cover error handling and all of that don't worry this is now just about the type conversion and then we return an OK with the num value here we have to wrap it in OK because we return a result type now let's see how we would Implement that so again I will just go to the documentation and copy the function signature like that so we have the function signature and then we can over here see what we want to do so in case we get an i o error we want to convert that to our custom CLI error now how would we do that let's change here the argument to e for error and the type would be IO error right we are implementing it from this type now what we want to do is we just want basically to pass the i o error into the io error variant in our custom type so we can access the variant so we can create an instance remember this is the syntax to access the enum so we access CLI error and then the io error variant and all we do then is just passing this IO error type meaning we then have an instance of this variant holding our i o error and that means we have then implemented the from trade for our custom type so any IO error type can be implicitly converted to CLI error meaning in case this function over here returns an IO error it will implicitly be converted to a CLI error let's do the same thing over here for parse int error this would be the error type from the parse method so again let's change here the argument name and the type would be over here and then we again just access this enum access the pause int Arrow variant and we pass it the actual error that gets returned and this would mean we then create another instance of the pause error variant let's see if this works and as you can see this is compiling meaning in case of an error over here both these Arrow types from the standard Library I or error and parse int Arrow will be implicitly converted to CLI error right try from try into similar to from and into try from and try into our generic traits for converting between types unlike from into try from and try into are used for valuable conversions and return a result instead of a plane value now again we use the result type we will cover that when there is a possibility of an error of something going wrong so as you can see we have here an i16 holding 256. into trade has a method in two hence try into has a method try into right this is the method defined on the try into trade so we take this value here and call the try into method on it meaning this will return a result so we match this result in case of an okay then we unwrap the value from the result and if it is an error then we just print out the error over here and return 0. right so in case this succeeds then n will hold a u8 and the value in case this fails n will hold a value of 0. now we have seen that when we want to convert an u8 then this would actually fail because this is out of boundaries but because we are here basically error handling that means that the program here won't actually panic so in case this fails here which it does then we will just assign to n the value 0 and print out the error so in this case n would be zero and again error handling will be our next topic so don't worry if this seems a little bit strange then over here we have a tuple struct even num and it takes an i32 type we Implement here the try from trade for the custom even num type now here we have an Associated type and we Define that it should be of type unit type okay and then we Implement here the try from function so all this function basically does it takes a value and it checks if it is divisible by zero and then we will return the result in this case okay even num and inside it t value provided otherwise if it is not divisible by 2 then we return an error holding our unit type we have defined here as you can see the type in case of an error would be this Associated type error meaning a unit type so we have provided that over here so then we can use the drive from function so when we call even num and try from 8 then this would return okay right because 8 is divisible by two so we would get back a result in this case okay even num and T value we have passed right that's what we defined here now in case we try from a value of 5 this would return an error with a unit type inside because again 5 is not divisible by 2. now with try from and try into it's exactly the same like with from and into when we Implement try from then try into is automatically implemented so we can use this try into method here directly so we try here to convert eight into this result right and the result in case of success will will hold the type of even num and otherwise a unit type like defined over here and in this case result will then hold ok even num and the value we have passed eight now when we call try into on 5 then this will actually return error and a unit type inside like we have defined over here let's see and this is compiling see you in the next topic let's now see two more exercises and this time we are converting a custom type into a string so to convert any type to string you can simply use the two string trade for that type rather than doing that directly you should implement the fmt display trait which will automatically provide tostring and also allows you to print the type with print line so again same thing like with from and into if you implement from then into is for free so you usually don't implement this trade directly for a type you implement display and you will get two string implementation for free so let's see we import here fmt and we have here our custom type point now here we are instantiating a point so what we want to achieve here is that when we take the point instance here we have instantiated and converted to a string this should then be the output and we can also use the format macro here basically just providing origin because the format macro will automatically convert that to a string in order to print it out and the output will be the same so let's implement the display trade here so as you can see this is the display trade and it provides one method now let's take it from over here they provide here an example and we can just copy this over here now I remove the lifetime it's not needed and let's implement the fmt method here so as you can see the fmt method provides here a formatter meaning we want to write we can use the right macro here and the right macro works like println or format but the difference is it will write to a buffer in this case f this is the formatter from the fmt module and then we here provide what we want to print out so in this case we want to print out the point is entity values so let's do that and then like in a print Ln macro we provided the values so in this case we want cells dot X and self.y we want to print out the X and the Y field and that's basically it we have implemented the display trade for Point meaning our customly created type Point can be converted directly to a string so let's see and this is compiling so if we pass origin here let's see the output as you can see this would be the output so we can even manipulate how the output should look like so instead of just so instead of just printing out the two fields we have even provided the output with a whole sentence right this will be the last one we will be doing we can use parse method to convert a string into an i32 number this is because from string is implemented for i32 type in standard Library to use from string method you need to introduce this trade into the current scope so we have to import this trade from the stir module so over here we want to pause and parsing again means converting a string into an integer so we would here have an i32 type of this value and over here the same thing now we can either annotate the type over here or we could use the turbo fish syntax meaning we can directly annotate the type on the method like that and then because the from string trade is actually implemented for the i32 type we can also call the associated function from store let's do 20 here so this will convert from this string literal into an i32 meaning this will then hold an i32 now because all of them hold i32 integers we can then do a mathematical operation meaning we just add together all of these values 5 10 and 20. so to be variable will hold an i32 and it should be equal to 35 and this is compiling see you in the next topic now we will cover error handling anti-simples form of error handling is to use the Panic macro Panic will print out an error message unwind this deck and finally exit the program now unwinding the stack means that when you actually execute the program then we have seen that the local variables and the function calls will be pushed to this stack now when panicking that means that the compiler will actually delete everything step by step from this stack memory and then exit the program and in multi-threaded programs it will exit the thread in which the Panic occurs not the whole program so we don't deal here with a multi-threaded programs this will be another course but I just want to mention it so let's see exercises as you can see we have here a function drink which takes a string literally if the string literal is lemonade then we print out success otherwise we actually want to panic right because we want to ensure that this line never gets reached meaning never gets printed out so when we pass over here lemonade then this will print out success and then will panic and this would be the output if a program panics threat main panicked at explicit panic and it will provide you the line number at which the Panic occurred in in this case an explicit panic because we have here called the Panic macro common Panic cases make make the code work by fixing all panics so when we call here the S bytes method on a string then it will give us back the array holding the utf-8 bytes of this string now we have seen that actually the utf-8 representation of a would be 97. B would be 98 and C would be 99 so again if srtq doesn't match if this does evaluate to another value then this then assert EQ will cause a panic over here we have a vector and we try here to access the third index now we only have now we only can go to index 2 right it's the outmost index we can provide so let's change that and this will then hold an i32 right because we are accessing a specific element unwrap May panic when get return a none so the get method over here will return an option meaning it either returns a sum with a value or a non now in this case this would return a non because we don't have any value at index 3. so when we try to unwrap a non value then we will panic now let's hear excess index 1 for example then it will return a sum two right the first element write the element at index one and unwrap we basically just unwrap the inner value so La will then hold a value of 2 of type i32 sometimes the compiler is unable to find the Overflow errors for you in compile time so a panic will occur so when we call here the production rate per hour function with a value of 2 then let's see we have here speed the provided argument of type u8 and here we have cph of type u8 now as you can see we are inside here multiplying the speed in this case 2 by cph 222 now this would actually result in an overflow because 2 times 221 would be 442 right now remember an u8 can only represent up to the number 255 so this would actually cause a panic but what we can do is just actually taking here a u16 and converting that to u16 meaning when we multiply these together then the result of u16 will still be able to represent the resultant number and over here we can do the same thing and then let's see because of the same reason as above we have to wrap it in a function to make the Panic occur so here we are calling the divide function with two arguments 15 and 0. so to divide function takes two arguments of type u8 and just divides it now this would also panic because we can't divide by zero this is a mathematical Rule and this will always lead to a panic so let's here divide by 1 for example let's see if this is compiling all right and again when we use the get method we will get back a reference to the value and not the actual value so let's change that and this is compiling detailed call SEC by default is stack unwinding will only give something like this so this would be the output if your program panicked though there is the reason of panic and the line of code is showing where the Panic has occurred sometimes you want to get more info about the call stack fill in the blank to display the hole called stack tips you can find the clue in the default Panic info so when we run cargo run this is basically how you execute a program you have written and will cover that then you just provide over here this rust back Trace equals one environment variable and it will then give you detailed information about the Panic that occurred so over here unwinding and abort by default when a panic accuracy program starts unwinding which means rust walks back up to stack and cleans up the data from each function it encounters but this work pack and cleanup is a lot of work the alternative is to immediately abort the program without cleaning up if in your project you need to make the resulting binary as small as possible you can switch from unwinding to aborting by adding below content to cargo.com so we can here Define in this cargo.tamil file and I will show you an example of that how Panic actually behaves so we have two um so we have two options here either unwinding or aborting when choosing a board here then the stack won't get unwinded meaning this will then be the responsibility of the operating system and not of the rust compiler alright see you in the next episode so we have seen the option type and the option type stands for a value that could potentially be absent so let's now look at the result type result is an enum type that represents the outcome of an operation that could potentially fail it has two possible variants okay a value T was found and error and error was found with a value e and as you can see the variance hold the specific information the expected outcome is okay the unexpected outcome is error since result is an enum the possible variance can be matched using a match pattern and that is what we call error handling so let's see an example let's again consider the divide function we have seen before so again we can't divide by zero meaning if the second argument here is 0 then we should return an error otherwise the program will Panic now we can do here error handling meaning we check if Y is zero right or in this case 0.0 because the arguments are floating Point numbers now in case y what's the value of 0 then we return the error variant of the result enum and we provide it an error message all right so in this case we use an explicit return keyword meaning when this returns it won't execute any further it will directly return the error type and stop execution now in case Y is not 0 then we will just divide X by Y and wrap it inside okay because remember result is an enum meaning we have two wrapped the resultant value inside the variant so that means when this function succeeds then we will get back an F 32 right the result of this operation and this is wrapped inside okay because as you can see we are returning a result type now in case of an error we would return this string over here wrapped in the arrow variant and this allows us then when we call the function divide with two arguments that we can then match the result we get back here as you can see we would get back a result type meaning the result type has two variants either okay or error and we can here use pattern matching to the structure the inner value for example this F32 into the variable Val which we then can print out and in case of an error we will get back this string over here the error message and we destructure it into this variable here and then print it out unwrap the unwrapped method takes as input a value of type result and takes out the value which is wrapped inside okay in case of success or panics in case of an error in case it returns an OK variant we just take the value that is wrapped inside otherwise the program will panic so you should use unwrap only if you're a hundred percent sure that the return variant will be okay otherwise you should use match and let's see the question mark operator the question mark operator is a shorthand way to propagate errors or unwrap OK results basically it's the same as unwrap but instead of panic it returns an error it replaces an entire match statement and can and it can even be used in the main function so as you can see over here in the main function we can Define here that main should return a result type and as you can see we have here a string holding a number then we try to parse this string over here into an i32 type now we have seen that the parse method could potentially fail right if we have here a string holding T for example the compiler can't convert it to an i32 because it's not a valid number so in case we get back the OK variant remember parse will return a result if we get back okay then we just take the inner value and return it meaning the inner value will then be assigned to the number variable in case of an error we want to return meaning this over here won't get executed it will return before and we just provide the error message over here and in this case because we are using a function from the standard Library this error message is actually provided right so we just take the provided error message and return it and if this didn't fail it means this will get executed and we just print out the unwrapped number now because we return here a result type we have to also return an OK in case everything succeeded now because at the end of Main basically the end of the program has been reached so it doesn't make sense to return any meaningful value so we just return a unit type like that so I've told you that the question mark operator actually replaces an entire match statement meaning this over here would be exactly the same so let's see again we are in Main and we return a result now as you can see we have again a string holding a number then we try to parse over here and again pause returns a result so we have to handle the error case now we can do that appending to it the question mark operator meaning in case this succeeds and it returns an OK with the value in this case 10 but post as an integer then it will just assign it to this variable meaning number will then hold 10 as an i32 in case this fails then it will return a pause int error as defined in the parse method okay so this over here is exactly the same like we did over here and when this returns an error the following lines won't be executed in case this succeeds then we have number which holds 10 of type i32 then we'll just print out the post number and then we return a unit type because remember we are here returning a result meaning we have to also return OK variant in case of success but again because we are in Main and this would be the end of the program we don't return any meaningful value we will return a unit type and there is also a concept called type Alias so it's basically just a way giving a name to an existing type so if you like to write u64 like this then you can create a type Alias meaning we can use the type keyword and provide a new name for a specific type and as you can see over here we are then using the new type we have created here and this would at compile time then be replaced with the real type and please don't confuse that with Associated types in trades they are different okay let's see result is an enum to describe possible errors it has two variants okay and error in short words the expected outcome is okay while the unexpected outcome is error so let's see fill in the blanks and fix the errors first of all we are importing the pause and arrow from the num module because we are using here the parse method meaning it will in case of an error return the parse and error now we have here the function multiply that takes two arguments of type string literals okay now as you can see we are here returning OK meaning the return type will be of type result and in case of success or let's actually wait with annotating that let's first see what's happening here so we are taking this string literal and try to parse it to an i32 right so again this parse method over here will return a result in case of success we would have okay and the value that was paused so for example when we call here multiply with an argument of 10 for the first parameter then we would have a parsed number 10 right so we would have in case of success and I 32 here in case of an error we would have the parse into error like that and parsing terror is the error type that is defined for this method again this is implemented in the standard Library so this has to comply with what is defined there and N2 is exactly the same but for the second argument then over here we unwrap and remember unwrap will take a result and just unwraps the inner value so in case we have okay let's take the first argument 10 then unwrap we'll just unwrap the inner value meaning it Returns the integer i32 right that is wrapped inside the result enum and then over here we are multiplying by N2 again we have to unwrap because this is a result type and if this succeeds actually then we will return an OK meaning OK will then hold in this case 10 times 2 will be 20 right like that so we return an i32 in case of success in case of error we we will return a pause and error because that is the error that is defined from the parse method so let's see when we call multiply with these two string literals 10 and 2 then this should actually work right because the compiler is able to parse these into integers so it will pause here and this will then be okay 10 and over here we would have okay 2. then we unwrap both of these over here meaning we unwrapped the value from the variant and here are two meaning we multiply 10 by 2 and then this would be 20 and we wrap it inside okay because we have to return a variant of the result enum which means that the return type over here would be this right so result in case of success will hold ok and 20. right now in this case this would actually fail because the compiler can't convert T to an integer now we want this to actually assert to 8 so let's do here four and this means this will get passed to four and this will get paused to 2 right and then again we just multiply these together and return it wrapped inside the OK variant meaning over here we get back a result and result will then hold ok and eight right now if we unwrap the result over here then it would unwrap the inner value as you can see so unwrap method allows us to unwrap the inner value so okay 8 would be unwrapped to eight question mark operator is almost exactly equivalent to unwrap but question mark operator returns instead of panic on error so let's see we have again the percent error Implement multiply with question mark don't use unwrap here so let's see again we are taking two arguments of type string literal right three and four in this case and let's start to implement so we declare here N1 and we take N1 string and try to parse it into an i32 right now again we should here use the question mark operator so we append it after the method call here and then we can only use the question mark operator when the return type of the function is a result right because the question mark operator in case of an error will return the error variant and inside it the error value so we return error a result in case of success it should be i32 and in case of failure it should be dispersed in error because again this is defined as the error return value of the parse method now for N2 we do exactly the same and that means if any of these fail then it will immediately return meaning the rest of the function won't get executed and a pause end error will be returned now the question mark operator in case of success it won't return a result it will unwrap so for example when we pass here 3 and this will get passed it will return okay and three right and then the question mark operator like unwrap will unwrap the inner value so N1 will then hold three meaning an i32 integer and N2 the same thing and then all we want to do is just returning and OK in case of success because the return type is result and we want to multiply N1 by N2 notice over here that I didn't have to unwrap because we have here I 32 types right while over here we had to unwrap because this held result types you have to remember that let's see and of course I have to change that over here typo and this is compiling so what we had over here when we provide Forum as a string we would have okay four right and then the question mark operator unwrapistic value and then over here we are multiplying three by four which would be 12. then we wrap it inside ok to comply with the function signature so this will then be the returned value right and then over here as you can see we are calling the function and unwrap it because we have here a result type so we unwrapped the inner value because this would return okay 12. and we unwrap it meaning the inner value gets unwrapped let's see over here we have the file type from the fs module and over here we are importing some stuff from the i o module so over here we are opening a file providing here to the open Associated function the name of the file in form of a string literal meaning this will then return a result and in case of success it will hold a file right and in case of error it would be IO error all right and then we are matching f now F holds a result meaning we must over here handle both cases from in case of success and in case of failure so if it is okay then we unwrap the inner value meaning the file and we just assign it to f in case of an error we return the error wrapped inside the error variant of the result type right because this function over here returns a result all right and then over here we are creating a new empty string we then take F over here and read to string meaning the contents of this file over here will be copied to this string meaning s will then hold basically the contents of the file we have opened and we have to provide here a mutable reference because this is defined in the standard Library and then again because this read to string method returns a result we have to match it so in case of okay then we will return over here a string right this string s over here in case of error we will return the error and both of them file open and read to string return an i o error all right now over here fill in the blanks with one code line don't change any code lines as you can see we have here the function read File 2 which returns a result in case of success it's a string in case of error it will be the i o error from this standard Library we here declare a new string and we here initialize a new empty string and I told you that the question mark operator replaces a whole match statement so we can write all of this in one single line so first of all we are opening the file so let's take this and as we've seen open will return a result so we can use the question mark operator here to replace this whole match statement so in case of error it will immediately return and it returns this error type in case of success then we will get OK and a string and this will then unwrap it to the string right and in case of success we will have a value of type file like we saw over here and again file is a type defined in FS module so we have seen that the question mark operator will then unwrap we will basically get something like that and it will unwrap the file inside the OK variant what we then can do is call the read to string method on it right so we are calling read to string and then again we have to append this question mark operator because we need to string also returns a result type right so in case of success we'll then over here mutate s meaning the contents of this file will get read to this string all right or written to the string and then we just return s right the string and that means in case of success we will have as return type a result that is holding a string in case this or this fails then it will return an i o error meaning if this fails then over here a i o error gets returned and this will never be executed and if this fails again we return this error and this will never get executed let's see this is compiling map and then map and then are two common combinators for result t e also for option t so we have over here the parse into error fill in the blank in two ways map and then so we have here the function at 2 taking a string literal and returning a result in case of success holding an i32 in case of error parse and error all right because this is the default error type of the parse method then we are taking this then we are taking this string literal and try to parse it to an i32 and then as you can see when we provide here at 2 with a string holding four then we want to get back six so as you can see when we provide four we will get back 6 over here and that means we want to parse this string and then add 2 to the post number and for that we can use map which is really convenient because we not only want to handle this result case but also then modify the value right so n here takes a closure and we will cover closures very soon so we provide here an argument n and n would then hold the parsed number and all we do is adding 2 to n right and that's all so let's actually see the map method in the documentation as you can see maps a result te to result u e by applying a function to a contained okay value leaving an error value untouched so when we call the map over here it will perform it will apply a function if this method returns an OK value if it returns an error then it will leave it untouched all right so in this case this would actually succeed right so pause over here will pause this 4 into an i32 integer meaning it will then take the OK value and unwrap it so n will then hold A4 and then we just add and and then we just add 2 to 4. right let's see and this compiles now the second approach would be using and then now and then is very very similar to map the difference is here in the closure that the end then method takes so let's go back to map here so over here you can see this is the argument for the closure right and the closure over here returns directly the value all right and over here it will the closure itself will return a result now both of these methods return results over here but the difference is in the closure and you will see what I mean when we are looking at this so this is very similar it will take this past integer and then put the unwrapped value into n and then over here we add 2 to 2N now we have seen that map would work like that and and then method will actually return in the closure itself a result so we have to wrap that into ok like that but otherwise they are exactly the same and this succeeds number five with the return type we written we can use pattern matching without unweb but it's all verbose so here again we have the multiply function taking two string literals in case of success it returns an i32 otherwise parse and error so over here we are using the match keyword now we want to pause the first string literally the first argument in case of okay then we destructure it into T variable N1 so N1 will then hold the parsed number in case of an error we just return the error type then we match inside this match arm over here again so we match the second argument parsing it and if this is okay then we get out and two all right and then we can multiply N1 by N2 and return the result wrapped in the OK variant because as you can see the function returns a result type and in case of an error it just returns an error so this would be very verbose so rewriting multiply to make it succinct you should use both and then and map here so let's see we take first of all the n one star over here the first string literal all right then we want to parse it to an i32 integer right and let's then use and then so if this succeeds then we will have here n which holds the past integer right and let's actually write it more beautiful like that so we take then n or let's call it or let's call it N1 just to make a distinction and then over here we want to parse the second one right we want here the second one and then if this also succeeds we want to multiply N1 and the post and two together so let's use a map here and we would here have N2 right because map over here is called upon the parse method so we would then get back and two and then we just return N1 times N2 and notice because we are using map here we don't have to wrap it inside okay right so this will then return for example when we call it by 10 and 2 it will return 20. all right and and then we have seen that it will return the result type so in case of success it it will return okay with an i32 value inside otherwise it will return a pause end error let's see and this is compiling so as you can see when we are calling the multiply function with with 10 and 2 as string literals it will pause the first argument meaning we then have over here okay 10. right and then over here we call the end then Method All right so we take this N1 value here basically unwrapping it and inside and then we parse the second number so in this case two right so this will then get parsed into an i32 and we then can map meaning we have N2 over here and two in this case would be okay 2 right and map will take an unwrap the value inside OK meaning we then have N2 which holds two over here and then we multiply N1 by N2 and return it all right and all we then do is here The Returned value would be of this type right and in this case this would return okay 20. right because it could parse these two numbers and it multiplied it and returned it so we get okay 20 then we pass that to the print function as you can see it takes as an argument a result type then we match it if it is okay then we print out the value wrapped inside okay in this case 20 otherwise we will print out the error and when we call multiply with an argument that can't be paused then you would get a pause int error right so this would then hold a percent error we are providing that 2D print function and then we match the argument this TT and this will then match the error meaning the error will get printed so in the first one so in the first print call n is 20 so we have successfully paused these two numbers and unwrapped the value printing it and in the second one because we provide here an argument that can't be paused we get and pause into arrow and this would be the error message that is provided in this standard Library so type Alias using this type over here everywhere is verbose and tedious we can use Alias for this purpose so at a module level creating aliases can be particularly helpful errors found in a specific module often has the same error type so a single Alias can succinctly Define all Associated results this is so useful even the standard Library even supplies one IO result so as you can see we have here again the pulse and error and we want here to create a type alias now we have here the same multiply function we have seen above so I won't cover that anymore but the return type is defined as res i32 and the print function over here takes as an argument a type of rice i32 now as you can see exactly the same functions as we had over here but we don't write out the whole type we use here A type Alias so we can Define that here we use res i32 and we want to Define that as a result in case of success holding an I30 tool in case of an error as you can see the parse method would return a parse and error and then instead of writing that all the time in our functions we can use this simple type alias and of course at compile time these type aliases will then get replaced with this concrete type here let's see and as you can see the same output as before using result in F and Main typically the main function will look like this however main is also able to have a return type of result if an error occurs within the main function it will return an error code and print a debug representation of the error debug trade the following example shows such a scenario so as you can see we have here again the percent error and we Define here in the main function that the return type should be of type result in success returning a unit type in case of error it will return a parse and error so over here we have a string literal and we are then trying to parse this string literal to an i32 now again pulse returns a result so we have to handle both cases in case of ok we just take the wrapped value and assign it to number right in this case this would hold an i32 in case of an error it will just return the error and it will stop execution from this point and then all we do is we will print out the number so let's see if this is compiling as you can see this is compiling we are printing out the number and don't forget because we return a result type here we have to return the result variant okay with a unit type in case of success we provide here a unit type to the OK variant because the end of main means the end of the program because the end of main means the end of the program so we don't have to actually return anything here right because if the program ends this returned value won't be beneficial but we can just provide the unit type so we comply with the function signature and by the way as we've seen we can here just use the question mark operator so we can replace this match statement by this and this is actually the reason most people use here in main the result type because then it allows us that we use the question mark operator otherwise if we didn't have result here we can't use this operator let's see of course I have to remove the match keyword here and as you can see this would be the same thing all right see you in the next topic let's take a look at cargo now cargo is the official package manager and building tool in Rust it helps automate tasks such as creating new projects building running testing code and managing dependencies a crate is a compilation unit of rust source code so basically a crate is the smallest amount of code the RAS compilers considers at a time and crates.io is the repository for rust packages so as you can see this is the website of crates.io and here you find some libraries for example we have here the rent Library and this allows for creating random numbers and as you can see you can add this very quickly into your own project and we'll see how this will work what is a crate a crate can be of two types either it's a binary crate and that means the program is compiled into an executable binary it's basically a program you can run and binary crates always have the main function in it and a library create a library create is compiled into a library and it doesn't contain a main function but it's considered a collection of reusable code that can be shared across multiple projects and the create route is the source file that is the root module of the crate in binaries it would be source main.rs and in libraries it would be the file lib dot RS then we have modules and a module is a way of organizing code by grouping together related items modules can be imported using namespaces avoiding naming collisions it also controls the privacy of its items like functions trucks enums and so on when compiling the compiler starts from the create route we have seen in a binary crate this would be in main.rs and in a library it would be lib.rs then it checks if the modules are declared and looks for sub modules sub modules could be directly written in line with curly braces it could be written in a separate file which has the module name ending in dot RS or it could be in a separate director which has the same name of the module and a mod.rs file inside it and we'll do exercises and you will see how that works so in case you want to follow these exercises you actually have to install rust on your system so you can go to rosslang.org and then you can click install and over here it will give you a command you can copy then running a terminal and you can just copy this into your terminal now I'm on a Unix like operating system meaning I will use the Linux command but the website will recognize your operating system and provide the appropriate command so if you're on Windows it will give you a command that you can paste into Powershell for example so proceed with installation default you can then press 1 and enter now I have already downloaded that so I want um run it again and then to check if everything is installed correctly you can use these commands first of all we check is the RAS compiler installed if you get back a version it means everything is installed then we check for cargo this is fine and rust up and rust up is used for um updating the version of rust on your local system all right all right so let's now see the exercises a package is a project which you create with Cargo in most cases so it contains a cargo.tomel file in it so create a package with below layout now I will do that directly in the home directory but you can do it at any location you want so to create a new package we can use cargo new and then provide the name of the package so we want the name here to be hello package and as you can see that we and as you can see the package has been created so let's C CD hello package and we are now inside our created package right so we have here cargo Tamil and a source directory let's see source and if I print out the contents in main as you can see it contains a simple program that prints out hello world so let me actually do it like that so over here if I now do A3 command as you can see we have exactly the same file structure here we have a cargo Tamil and then resource directory with main.rs inside let's print out the cargo toner and as you can see this matches the file in the exercise create a package with below layout so over here we want to create a library so I'm back in my home directory and we can create the library create right now let's do Park on you then the package name here and then we have to write the lib flag right so this will then create a library and then let's CD into that as you can see we have corgotomel and a source directory now let me print out the cargo thermal file and as you can see again this matches and if we go to the source directory as you can see over here we have a lib.rs file and this just has a basic add function over here together with some tests all right fill in the blank with your answer what's the difference between package number one and package number two now the difference would be package number one is a binary crate and package number two is a library crate all right create a crate is a binary or library to create rules the create route is a source file that the rascompiler starts from and makes up the root module of the crate in package hello package there is a binary create with the same name as the package hello packet and source main.rs is the create root of this binary crate similar to hello package hello package one also has a crate in it however this package doesn't contain a binary crate but a library crate and source lib.rs is the great root so what's the name of the library crate in package hello package one the answer would be it would be hello package one right because as we've seen over here in the package hello package there is a binary crate called hello packet and that means in a package hello package one the name of the library crate would be hello package one and by the way a package is what we have seen over here basically when we create a new project right when we create a new project we create a new package and the package consists of a cargo.tomel file and a source directory together with other stuff add a library create for hello package and describe its files 3 below alright so we want to add in the hello package a library crate so let me remove this over here and we will go inside hello package now over here in this Source directory we want to create a new library create as you can see we have until now a binary crate so let's create a lib dot RS touch is a Linux command that allows to create a new file so then we would have lib RS and Main RS so when we look at T3 as you can see we then have the cargo terminal The Source directory and two crates one Library crate and one binary crate so this will then hold lip.rs and main.rs after this step there should be two crates in package hello package a binary crate and a library crate both with the same name as the package a package can contain at most one Library crate but it can contain as many binary crates as you would like by placing files in Source bin directory each file will be a separate binary crate with the same name as the file so create a package which contains three binary crates hello package main one main two one Library create so what we want over here is to have in this Source folder the lib.rs and main.rs then inside the source folder we want to have a bin directory which holds main 1 and Main two right so so as we've seen we can have as many binary crates as we want in a package but only one Library create and over here we have some other directories so we have tests for integrated tests benches for Benchmark and examples so let's create these over here so as you can see inside source we already have a library crate and a binary crate now we want to create a new directory called bin then we see the into bin and we create main1.rs and main2.rs so again when we go to the root of the package as you can see we have cargo terminal The Source directory we have inside the source directory a bin directory holding two binary crates main one main two and we have in the source directory lib.rs and main.rs yep as you can see the above package structure is very standard and is widely used in many rust projects so let's see modules modules let us organize the code within a crate into groups for readability and ease of reuse module also controls the privacy of items which is which is whether an item can be seen by outside code public or it's just an internal implementation and not available for outside code private we have created a package named hello package in previous chapter and it looks like this now we actually created over here a bin directory so let's delete that I go into source and then remove the bin directory so as you can see T3 looks exactly the same now it's time to create some modules in the library create and use them in the binary crate let's start so as you can see we can Define some code in the library create and then use it in the binary crate so Implement module front of house based on the module tree below so in the library create we want to create a module front of house and this should then hold two sub modules hosting and serving now hosting holds two functions and serving holes for functions so as you can see we will do that in the library crate all right and again modules allow us to structure our code so let's open lib.rs and I will copy this over here so we have to implement front of house now we have seen that front of house module should contain two sub modules namely hosting and serving like that so we have a front of house module that contains two sub modules hosting and serving now we want to add these functions over here add to wait list and see it at table in the hosting module uh at waste list adds to wait list and see it at table so as you can see front of house containing one sub module hosting holding two functions over here now let's see serving I will just copy that like that and doing it like that as you can see it is serving module holds three functions and when we run the code actually we can use the cargo run command and as you can see this is compiling but it gives us warning that we didn't use these functions over here which is okay and of course hello world will get printed from the binary crate right because main just prints out hello world let's call add to waitlist from a function eat at restaurant which is within the library create route so inside lib.rs we are below the module front of house we are creating a new function like that call add to wait list with absolute path so it's like in a file system you can have absolute paths and you can have relative paths so in this case we want to use the absolute path to the add to wait list function over here so we would use for the absolute path to create root then we want the front of house module and we want the sub module hosting right and then over here we can call the function like that add to wait list now this would be considered the absolute path now over here we are using the relative path so we are actually already in the create route right so that means we don't need to use crate here we can directly access front of house hosting and then call the add to wait list function now as you can see it gives me here an error module hosting is private so we can make this public using the pub keyword all right now the same thing with the functions let's make them public like that you can use super to import items within the parent module so again in lib.rs we want to create another module so let's copy that put it below the front of house and as you can see over here we have the back of house module with two functions declared inside it so we have fixed incorrect order and inside this function we call the cook order function over here fill in the blank in three ways using keyword super using absolute path so in order to access the function over here serve order let's first use this super keyword so super means we go one level up right so we are here inside the module back of house we go one level up which would be the create route right lib dot RS so we use super and then we can access front of house and I guess hosting no serving and serve order like that and again serving is private let's do that let's make it a public function and again serving here is private so let's make that public and also the function here like that and the second way would be using absolute path so again you can use here create right create would refer to lib.rs because it is the great root of this package separating modules into different files so as you can see we want to put these modules into their own distinct files please separate the modules and codes above into files resident in below third three so as you can see we have here Main and leap RS crates and here we have the modules we want to split up so we have the module front of house holding hosting and serving this r sub modules and mod.rs and we have over here back of house.rs now we have said that there are three ways to declare modules right the first way would be in the create route for example lib.rs we Define in module and put it right inside the curly braces right all the code is inside these curly braces right the second way would be two in lib.rs declaring the back of house module but without curly braces right only declaring like this for example only declaring the module and that means the compiler will then check is there in Source directory a file named as the same name as we gave it to the module right so we declared a module in lib.rs and then we have to create a file ending in dot RS with the same name as the module we have declared and the Third Way would be if we want the module to be a subdirectory of source then we have to again declare front of house inside lib.rs the same way we did before but we create a directory instead of a file with the same name as the module and then very important the code of the module will then be in mod.rs now in our case we are declaring in mod.rs2 sub modules hosting and serving all right so we want to have this code separated in different files and this is how it should look like so let's start with back of house all right I will copy that and first of all I will delete anything in here and let's declare the module back of house right now we have here declared a module as you can see the file is not found so we can here declare and back of house dot RS and then we can put and then we can put the code of the back of house module directly into this pack of house file removing this over here because we have already declared T module and as you can see back of house file now contains only the code related to this module again we have in lib.rs declared the back of house module then we have over here created a file that has the same name as the module right and in here we are providing the code like that and because we have already declared the back of house module we don't have to redeclare it inside here right now as you can see we also want front of house now the difference is front of house should be a directory holding two sub modules so let's first of all in lib we want to declare front of house module like that then we create the directory with the same name as the module foreign like that then the first thing is we create a file mod.rs this is now the create root of this this will be considered the root of this module mod.rs and as you can see we want here inside front of house two sub modules hosting and serving let's declare them like that and then we can create both of these files let's start with hosting and we just take all the code from the hosting module here like that and let's create serving and we just take the code here from this serving module like that let me remove the comment so and now if we go back to the source directory as you can see we have back of house this is a module we have front of house hosting and serving these are two sub modules and we have here mod.rs exactly like we wanted here now over here by the way this function eat at restaurant will be directly in the lib.rs so as you can see instead of defining the module and putting all the code in curly braces meaning inline we can use um different files to restructure and reorganize our code and this makes the lib.rs file much more clean and readable assessing code in library create from binary crate please ensure you have completed the fourth exercise before making further progress you should have below structures and the corresponding codes in them when reaching here so we have done that now we will call a few Library function now we will call a few Library functions from the binary crate so let's copy that into the binary crate like that now we want to call a function so the output should be sit down please and then a second function which returns yummy yummy now as you can see over here we have to call the eat at restaurant function to get back yummy yummy and sit down please would be in the front of house module in hosting and seat at table function so let's see first of all I want here the module front of house right because the binary crate and the library create are separate from each other so the binary crate over here can't know what is defined in the library crate and then over here we can SD create module and then here we can use an absolute path providing the create route now we have to actually provide here the name of the crate meaning hello package all right and then we can call font of house and let's see front of house hosting seat at the table at table like that and then over here we call hello package and then eat at restaurant this is defined in our lib.rs so again instead of writing here a crate we write the name of the package because we are here in the binary crate so we can use the create keyword in lib.rs but in main.rs we have to write the name of the package let's run that and as you can see we have some errors module serving is private and module hosting is private so to actually access that from the binary crate we have to make them public so let's see first of all we go to lib.rs and we want these two modules to be public so let's make them pop like this which means that these two modules are now accessible for our binary crates the main.rs file then let's go to um front of house and let's here make these two modules or these two sub modules also public like that and if the module itself is public that doesn't mean that the inner functions for example or structs or anything that is inside the module is also public so we have to declare here as you can see this is already done that these functions are also public if we want to access it and as you can see serving also has the public functions now let's check again in lib.rs because we are calling here a function all right this is also public let's run it again and as you can see this is actually compiling it just over here and complaints that we didn't use some of the functions which is okay at the moment now instead of just assorting let's actually print that out and let's do it like that as you can see the output will be sit down please let's call the second one so we want to get this like that let's see all right I copied here the holy uh I copied here the whole assert we don't need this actually let's see and as you can see the output will be sit down please and yummy yummy so we have successfully restructured and reorganized our whole code into different files and folders meaning it is much more organized and much more readable all right see you in the next topic we will now take a look at the debug and display trades so types which want to be printable must Implement either debug or the display trades and automatic implementations are only provided for types in this standard Library so this doesn't apply to our customly created types a debug trade can be implemented by simply using derivable trade and a display trade must be manually implemented let's see we have here print Ln and format printing is handled by a series of macros defined in standard format some of which include format write formatted text to string print same as format but the text is printed to the console print Ln is the same as print but a new line is appended ePrint is the same as format but the text is printed to the standard error and e-print Ln same as e-print but a new line is a pendant all parse text in the same fashion all porous text in the same fashion as a plus rust checks format correctness at compile time so let's see we have here a string literal and we use here the format macro meaning the return type will be string and we want here s to hold a string of hello world so again we use the format macro like we would use the print L and macro we have been using so in this case we want to take S1 over here and then just append the rest so we take hello this will be placed here and then the rest and this is compiling let's actually print that out as you can see this would be the output when printing out s print print Ln fill in the blanks to make it print a hello world I am and turn on the new line Sun face so over here we want to use print because as you can see I am should be on the same line so as we've seen print macro doesn't append a new line character now a new line is defined in C like that all right and this is a symbol that the compiler knows that it should treat it as a new line but in this case print doesn't append it so the next line will be on the same line as this print statement so let's use here print Ln because as you can see after I am we want to have a new line and then over here we can again use print Ln and this would be the output debug and display so all types which want to be printable must implement the standard format formatting trade debug or display automatic implementations are only provided for types such as in this standard Library all others have to be manually implemented let's first look at debug the implementation of debug is very straightforward all types can derive the debug implementation this is not true for display which must be manually implemented and we have used that before this is the debug notation must be used to print out the type which has implemented the debug trade so as you can see over here we have a custom type this structure cannot be printed either with display or debug to make this struct printable with debug we can derive the automatic implementations provided by rust so again we can use a derivable trait meaning this struct over here implements the debug trade meaning we can print it out using debug notation so fill in the blanks and fix the errors as you can see we have here a custom type structure holding an i32 value types in standard and rust have implemented the debug trade so as you can see this is of type i32 now all of the types in the standard Library have implemented debug or even display so we can use here to display notation and this will print out fine now over here we must use debug notation because this is a type that we have created and again to use the debug notation we have to derive the debug trade like that and that means structure implements the debug trait and we can then print it out so this i32 type will be printed automatically because it's a type defined in this standard library and over here we just had to derived debug trade and then use debug notation here so format debug definitely makes one type printable but sacrifices some Elegance maybe we can get more elegant by replacing this notation with something else but not this alright let's see we have here a custom type that derives the debug trade then we are here instantiating it and when we now execute the program as you can see the output will be like this now we want the output to look something like that and we can for that use the pretty notation just adding this hashtag symbol between the colon and the question mark and then the output should look much more pretty as you can see the output will then be like this and notice we first had to derive the debug trade in order to print it out using debug notation we can also manually Implement debug trade for our types so as you can see we have here two custom types structure and deep now structure holds a type of i32 and depos a type of structure meaning these are basically nested so we have keep which holds a structure which holds an i32 right the problem with derive is there is no control over how the results look what if I want what if I want this to just show A7 so as you can see we are here instantiating it with a value of 7. now when we print it like that then as you can see the whole structure will get printed out but we only want tip value over here so what we can do is we implement the debug trade manually meaning we can manipulate the output so let's see the debug trade in the documentation and as you can see this is the debug trade as defined in this standard Library so let's copy this function signature so we can then over here implement the debug trade and this is found in a module M and this is inside the module fmt from the standard library and we want to implement it for the Deep over here now we remove that and we also remove that because we are implementing debug trade manually right we don't need the derived implementation so let's see deep then I will copy the function signature so here we can use a custom type fmt result and you know what we can use a namespace here so then we don't have to write here standard all the time like that I can delete this lifetime and now we can implement the debug trade namely this fmt method so again we want to write to a buffer so we will still write macro and it should write to this formatter which is provided by the debug trade and then over here all we want to actually print out is in this case self right the instance over here and then we access the first element so when we access the first element in a tuple we use the dot notation like that and inside here we have now accessed structure and we want to access this value this structure struct holes right so we again use zero and that's basically it now it should print out and of course over here fmt formatter this is also from the fmt module and as you can see now 7 will print so all we did over here is manipulating how the output of the debug trade looks like when we use debug notation and as you can see we have defined for the Deep custom type over here how the output should look like so the output will just be the inner element of deep and structure right this i32 and we just access that and write it to this buffer all right display here debug is simple and easy to use but sometimes but sometimes we want to customize the output appearance of our type this is where display really shines unlike debug there is no way to derive the implementation of the display trade we have to manually implement it another thing to note the placeholder for display is this and not that so this would be for debug trade for display we are using that and you know that already so make it work as you can see again we are using the fmt module and we have a custom type 0.2d holding two fields of type f64. now we want to implement the display and the debug trade for this custom type meaning when we instantiate this point 2D then we can pass the instance over here and it will give us this output right so we are using here the display notation and debug notation and as you can see we can here Define different outputs so when we pass an instance of point then the display trade actually will output something like that if we use debug notation then we want the output to look something like that and that's what I mean when I say we can manipulate how the output of a custom type will look like so I will take over here this method and I will just copy it over here now let's first implement the display trait as you can see we want the output to look something like that so we Define here lay the X field the value of x field and then plus the value of the by field and we have here an i okay like that and we can access these fields using normal notation self dot X and cell dot y right and for debug notation we want the output to look something like that so let's copy this put it inside here and as you can see we want here the value of the X field and here the value of the Y field and as you can see we are here using curly braces and this is usually for the placeholder so what we have to do here is using two of them meaning this calibrases over here will basically be escaped all right and then over here we have to provide the arguments self.x and self dot y let's see and this is compiling now let's see the actual output as you can see I'm using display notation and debug notation and just provided an instance of the point to the type and as you can see this will then be the output so question mark operator implementing fmt display for a structural whose elements must be handled separately is tricky the problem is each right generates a fmt result which must be handled in the same place fortunately Russ provides the question mark operator to help us eliminate some unnecessary codes for dealing with fmt result so let's see again we are using the fmt module and we have here a custom type a struct list a tuple struck list holding a vector of i32 elements right over here we are instantiating this list with a vector holding one two three now as you can see when we put this list over here into the format macro then we want the output to look something like that so we want to print these square brackets the index colon and then the actual value now let's see how this would be implemented notice over here we are implementing the display trade so we can then use it to display the type directly so as you can see this is the function signature from the fmt module and as you can see over here we return a type of fmt result now this is a third now this would be a type Alias and we have covered that now over here we want to extract the value using Tuple indexing and creating a reference to back so the first thing we do is we want to access the vector in the list struct and we do that using Tuple indexing right we want the first element because there is actually only one element on here so we access that meaning we'll get back a reference to a vector holding i32 types right here we are taking a reference now then we have basically unwrapped this Vector into this variable now as you can see we use the right macro to write to this buffer this square bracket because as you can see this is what we want to Output so the first thing added to the buffer will be this square bracket then iterate over V in vac while enumerating the iteration count in count so we take this vector and we iterate over it and enumerate meaning we will get back a tuple holding the index and the actual value then for every element except the first add a comma use the question mark operator to return on errors so as you can see if count is not equal to zero meaning if it is in fact the first element then we don't add this comma all right because we don't want to look something like that so this is only after the second iteration that this comma will be added and then we write the actual value over here and this iterates three times right for each element and then in the end it will put into the buffer this square bracket so when we run that as you can see the output would be something like that but this is the output that we want so what's missing over here is we didn't include the indexes right so what we can do over here we want here to be the index and then a colon like here and a space and then the value so we have the index in this count variable so let's use that and again we can then run the program and this succeeded so let's actually print that out as you can see this would be the output so we can really manipulate the output of our custom types using the debug and the display trades and by the way as you can see we are here using the question mark operator because this over here could potentially fail right that's why we are using this here and we use here the result type from the fmt module just in case any of these operations here fail alright see you in the next topic so let's take a look at lifetimes now lifetimes are only necessary when dealing with references and even then most of the time the compiler can actually infer the lifetimes and we don't have to worry about it so let's see a lifetime is another kind of generic ensuring that references are valid as long as needed every reference has a lifetime which is the scope for which the reference is valid most of the time this is implicit and inferred so we don't have to worry about it sometimes though lifetime annotations are needed and that's the case if the compiler can't infer it and lifetime annotations is a concept which most other programming languages don't have so even for experienced programmers this sometimes looks a little bit confusing but in actuality it's a very simple concept and you will understand it very quickly so actually the main aim of lifetimes is to prevent dangling references also called dangling pointers so let's see we are here in Main and we declare a variable R we don't initialize it with a value but just declaring it meaning the compiler will actually infer a lifetime of a for this variable and the lifetime a in this case is valid until the end of the main scope right then over here we have an inner scope and in this scope we are initializing variable x with a value of 5. so the compiler will annotate this variable to be of Lifetime B and as you can see lifetime B is only valid until the end of this scope now then we are assigning a reference to x to the variable R meaning the variable R we have declared here holds a reference to the variable X the problem here is that when X goes out of scope at this point then R Points to something that is not valid anymore right and that's the aim of references to avoid exactly this scenario now to understand lifetimes you have to understand the borrow check or in Rust so the borrow Checker Compares Scopes to determine whether all borrows are valid it's a key part of Russ ownership system it tracks lifetimes of references and ensures that they don't violate the ownership rules these rules ensure that the value is not accessed after it has been moved or freed from memory an important a reference to a value must never outlive the value itself so you have to remember especially the last line a reference to a value must never outlive the value itself so when we go back here as you can see we have here a reference to value 5. now the problem is that this reference outlives the value itself meaning X holding this value we are referencing to goes out of scope meaning the reference on here lifts longer than the actual value it is referring to all right let's see the compiler uses lifetime to ensure all borrows are valid typically a variable's lifetime begins when it is created and ends when it is destroyed so the scope of Lifetime annotate the lifetime of I and borrow 2. lifetimes are annotated below with lines denoting the creation and destruction of each variable I has the longest lifetime because its scope entirely encloses both borrow1 and borrow two the duration of borrow 1 compared to borrow 2 is irrelevant since they are disjoint so when we look at this program here what you see is that we have here initialized variable I right now this variable I over here lives until the main scope ends all right basically it outlives this scope and this scope now over here we are assigning a reference to I to borrow one right so borrow one lifetime starts and then we are just printing out borrow one now over here borrow one will go out of scope but it doesn't matter because the value itself still lives on all right and over here the same thing we have power 2 which holds a reference to I meaning that borrow 2 lives inside the scope so we print here borrow two and then it will here go out of scope but again the value here lives on so the actual value outlives T borrows or the references all right example two so over here we have a variable X with a value of 5. and then over here we have a reference to X assigned to variable r and then we are printing out R right so if we execute that it will compile because again as you can see we are declaring here the variable X in this scope so variable X we live until the scope ends and the same thing for the reference to X so the reference to X is assigned to R meaning R will live until the end of this scope so the value and the borrow to the value are both valid all right and over here you can see the lifetime annotations the important thing here is that both the value itself and the reference to the value or valid right the reference here doesn't outlive the actual value it is pointing to annotate R and X as above and explain why this code fails to compile in the lifetime aspect that's actually the example we have seen so we are here declaring r and over here in this inner scope we declare a variable X and initializing it with a value of 5. then we are assigning a reference to X to R the problem here is X will go out of scope so R still points to dislocation but this over here has gone out of scope right so rust won't allow you to compile this program and you will get a compiler Arrow saying X does not live long enough right because the reference to the value lives longer than the actual value lives right meaning the lifetime of X is shorter than the lifetime of r or the lifetime of X holding the value is shorter than the lifetime of R holding the reference to a value and this should never happen so lifetime annotating The Borrowed cycle reuses explicit lifetime annotations to determine how long a reference should be valid but for us users in most cases there is no need to annotate the lifetime because there are several illusion rules before learning these rules we need to know how to annotate lifetime manually so ignoring illusion rules and that's the next topic lifetimes in function signatures have a few constraints any reference must have an annotated lifetime any reference being returned must have the same lifetime as one of the inputs or be static so over here we have a function print one and it takes as an argument a reference to an i32 type alright now this is how we would annotate a lifetime it's actually very similar to generics but we use this apostrophe syntax and a lowercase letter by convention just going from a b c d and so on so in this case we annotate here that the reference that the reference here is of Lifetime a so when we have over here a lifetime annotation then we can provide here then we can annotate it here for our reference right and again we are just dealing with lifetimes when we are dealing with references so in this case we say the reference to the i32 type should be of Lifetime a now when we annotate that to our function it means that the lifetime a must at least live as long as the function itself or we all or we could also say lifetime a must outlive this function so the past reference over here must outlive this function this is just ensuring that the reference passed here lives longer than this function meaning we can ensure that this function always deals with a valid reference right and all we do here is printing out X mutable references are possible with lifetimes as well so if you would have a mutable reference you would annotate it like that first after the Ampersand of course you will always put the lifetime annotation and then the mute keyword and then of course the type multiple elements with different lifetimes in this case it would be fine for both to have the same lifetime a but in more complex cases different lifetimes may be required so as you can see we can Define more than one lifetime over here we have lifetime a and lifetime B meaning the references pairs to this function could be of different lifetime but again lifetime a and lifetime B must outlive this function so the past references over here must outlive the function right and returning references that have been passed in is acceptable however the correct lifetime must be returned so let's take a look at the function pass X as you can see it takes two arguments and both of them are references now this would be of Lifetime a and this would be of Lifetime B again both of these references must outlive this function and we are just returning X which is annotated with a lifetime a meaning the return type will return meaning the return type will be a reference to an i32 with a lifetime of a right this would be the type that is annotated to the X argument here if we would here have y and we return y then we would do it like that right because we are returning a reference with a lifetime annotation of B foreign so as you can see we have here two variables holding i32 types and then we are passing to all of the above functions T reference to these two variables right because all of these functions over here expect a reference to an i32 type now in this case this program will compile as you can see this is compiling because we have here declared X and Y in the main scope meaning they are valid until the end of the scope over here right so t x and y references we pass to the functions outlive all the functions which is a requirement right the best references must outlive the function so we don't have to deal with dangling references and so let's see an exercise to understand it even more make it work by adding proper lifetime annotation so over here we have the function longest that takes two arguments of type string slice now we then test the length of these string slices and if x is and if x is longer than y then we return X otherwise we return y so if we go over here in the main function and let's do some variables as you can see we have two string literals long and longer now we would pass that to this function over here now it could be possible in this case the return type is known at compile time but sometimes this is decided at runtime and when we are passing these arguments to the function over here let's do that actually and notice we don't have to pass here a reference because string literals are already references all right now if we would compile that this would actually fail right missing lifetime specifier so what we want to achieve here is first of all we want to add a lifetime parameter like that and then we want to define the lifetimes of these references now in this case the function over here could either return X or Y right depending on which argument has the longer string right so sometimes it might be X that is returned and sometimes it might be y so we can't be sure if we annotate here a and B like that we don't know which one will get returned is it a or b it depends on the arguments right so what we can Define here is that X and Y these two arguments have the same lifetime meaning these two references share the same lifetime they must be declared in the same scope and both of them of course must outlive the function longest so in this case this is what we are providing right we are declaring X and Y over here in the main scope so when we call the longest function with these two references over here it means that these two references still live on even after this function call all right because again the reference of X and Y will leave until the main scope ends now because we have here defined that both of these references have the same lifetime it is clear that the returned string slice must have the same lifetime either X or Y right let's see and as you can see this is compiling in this case longest will return y because it is longer than this than the X string all right a must live longer than the function here reference to string Foo would create a string followed by a reference then the data is dropped upon exiting the scope leaving a reference to invalid data to be returned fix the arrow in three ways as you can see we have the function invalid output we Define here a lifetime parameter a and declare that The Returned reference should be of this lifetime now the problem here is we are creating a new string and returning a reference to it right we are returning a reference to a string the problem is this string was declared inside the function now when the function returns to the caller it means the string over here will get out of scope right because it is declared inside the function so again if we call for example if we call this function then we would get back a reference to a string right the problem is that the reference points to something that isn't valid anymore this string declared here got out of scope so X points to something that is not valid anymore so returning a reference to something declared inside the function is a really bad idea now what we can do over here to fix that and this is probably the easiest fix we just return the string itself right so then ownership will be transferred and X will be the owner all right let's print out X as you can see this is working because we are here declaring a string and then return it meaning the ownership of the string will then be returned over here right so X will become the owner of the returns string from the invalid function the second way to fix that would be returning a string literal right and a string literal actually has always a static lifetime we will come back to that but that actually means that this reference is of static lifetime meaning it is valid throughout the entire program all right because remember a string literal is hard coded into the binary so it lifts as long as the program lives so then over here we will get back a string slice right let's see and as you can see this is also compiling the Third Way would be over here create a string and then we can pass this string to the invalid output so in this case we want here to take a reference to a string right we can also write it like that because remember a reference to a string will be automatically inferred to a string size right so so we are then passing a reference to this string right and what we want to return here is s meaning we return here a string slice and we can annotate a lifetime parameter and then we will print out X let's see and this is also working because as you can see we have defined here a string meaning s over here is the owner of this train then we are passing a reference of s to this function right we annotate here a lifetime a meaning the reference over here passed to the argument s must outlive this function which it does right we have declared s here so even after this function call S still lives on right and then all we do is we again return as the past reference right so it will actually have the same lifetime as the past argument all right meaning we have here then a returned string slice all right Point refs takes two references to i32 which have different lifetimes A and B these two lifetimes must live at least as long as the function print refs so as you can see we are here in the print refs function declaring two lifetime parameters one for argument X and one for argument y again we are dealing with references meaning we care about the lifetimes if these would be owned types like so then we don't deal with references right and then we just print out X and Y to provided arguments make it work if function which takes no arguments but has a lifetime parameter a so over here we can see that we annotate a lifetime parameter a and we have here and i32 integer now error X does not live long enough so we assign to Y over here in reference to X the problem again is that when we annotate this lifetime parameter it says the lifetime a must outlive this function which it doesn't right because the reference over here because the value we are referencing to is declared inside the function meaning it will then be dropped at the end of the scope attempting to use the lifetime a as an explicit type annotation inside the function will fail because the lifetime of reference X is shorter than a a short lifetime cannot be coerced into a longer one so let's see in main we are here destructuring a tuple into the variables 4 and 9 right so 4 will hold 4 and 9 will hold 9. borrows of both variables are passed into the function so we are then calling the print refs function with references to 4 and 9 right this function over here any input which is borrowed must outlive the borrower in other words the lifetime of 4 and 9 must be longer than D then that of print refs so again when we annotate these lifetimes on a function both of the lifetimes must outlive the function or both of the references passed here must outlive the lifetime of this function which they do right so 4 and 9 are declared in main scope meaning they outlive this function over here so this should actually work so failed borrow contains no references to force a to be longer than the lifetime of the function but a is longer because the lifetime is never constrained it defaults to static so so as you can see we have here declared the lifetime a which states that the reference holding the lifetime of a must outlive the function this is not the case right because we are declaring the value inside the function itself now when we remove that actually this over here will then work all right let's see let's see structs make it work by adding proper lifetime annotation a type of road which houses a reference to an i32 the reference to i-32 must outlive borrowed so as you can see we have here a custom type a tuple struct holding a reference to i32 now in this case we have to annotate the lifetime and we do it like that and then we pass it to the reference like so and that means this reference here must outlive this struct or the instance we create from it right similarly both references here must outlive this structure again we are here annotating the lifetime and we say that both of them should have the same lifetime which makes it easier right and enum which is either an i32 or a reference to one so as you can see again we can annotate a lifetime basically like a generic type parameter and over here as you can see this variant holds a type of i32 so the enum will be the owner of this type we don't care about any lifetime annotation we only care about lifetime annotations when dealing with a reference like that now let's see we have here i32 and i32 and then we are instantiating the borrowed struct with a reference X right so as you can see this would be the borrowed struct and it holds a reference 2x which is i32 right now as you can see we Define here that reference of Lifetime a must outlive this struct or the instance of destruct right so X over here must outlive this instance which which it does right because it again it is declared in main scope meaning X and Y we live until this point here so they outlived The Borrowed struct and over here as you can see we pass here references to the X and Y field of the named borrowed a struct so this would then be an instance now again X and Y in this case first of all they are declared in the same scope so they can have the same Lifetime right they both live in the same scope and both of these variables will end at the same time so this will actually compile right everything over here is safe so as you can see we instantiate clear the either enum and one time with the variant ref holding a reference to X now again as you can see we have here a lifetime parameter a which states it must outlive the instance of the enum which it does right X is valid all the time and the second variant doesn't hold a reference we just pass it to single we just pass it T variable now because this implements copy it's an i32 it means that even then y will still live on so either want to take ownership it will take ownership of the copied value let's see if this is actually compiling and this is compiling make it work so over here we have destruct no copy type and and over here we have struct example and as you can see we annotate here the lifetimes A and B meaning over here the A and B field could hold references that have different lifetimes but they can also be the same all right now let's see in main a tight to FN main stack frame so as you can see we have here an i32 and over here we have declared a variable which should be of type example but we haven't initialized it with values all right we haven't instantiated the example struct now notice here we have another scope lifetime B tied to new stack frame so then we have here an instance of the no Copy Type struct and as you can see fix me so then we are instantiating example with the fields over here passing it concrete values as you can see we are passing it a reference to Bar a this and a reference to Bar B so the problem over here is that VAR B will go out of scope at this point right meaning the example struct we have instantiated over here will hold the B field which points to something that is not valid anymore as you can see we are here printing out example now if we would print it out in this scope then this would actually compile and of course this must be your 32 because it is a u32 field here as you can see this is compiling fine now even though we are here in a new scope right we have declared Bar B here in the inner scope but even then both of these references are valid right because we are here then using example in the same scope and we have noted that here that you that the lifetime of a and b are different right so the lifetime of bar a is actually longer than the lifetime of Bar B right because for a lives until the end of the main function while Bar B leaves only until this point but again this is safe and this is compiling because the VAR B we have created here and passed a reference to the example struct is valid if we leave this over here then this wouldn't compile right because vorby gets out of scope and we over here say that both A and B must outlive example all right so example is used here but not all references are still valid which would lead to a compiled error but what we can do also is just removing this scope here meaning everything is now declared in main scope so this would also compile right because all the references to these variables are valid let's see here we have again struck no copy type and the example struct we have seen before and as you can see denoting that the lifetimes of these references might differ but they can be the same it's no problem so let's see here fixed function signature we have here the fix me function which takes us an argument a reference to an example instance and it will then return the B field from the provided argument from the example struct right it will return this field over here meaning a reference to no copy type because that is what B is holding now over here we are instantiating no Copy Type and we are instantiating the example struct so we pass for the field a here a reference to one and for B we pass a reference to no Copy Type right and then we pass a reference this example instance over here to the fix me function now let's first annotate the lifetime parameter again we do a and over here we say that the past reference to the example instance must outlive this function right which it actually does because here we are instantiating the example struct and passing it to a reference and passing it as a reference to this function meaning example over here will live longer than the function right and we can then Define that no Copy Type must also outlive this fix me function all right which it does because again it's secured the main meaning the end of scope will be at this point so both the instance of example and the no copy type both outlive this fix me function and as you can see we can pass this same lifetime parameter because they are declared in the same scope meaning they have the same lifetime methods are annotated similarly to functions so we have here destruct owner holding an i32 type so annotate lifetimes as in a standalone function as you can see we have here the implementation block for owner and we Implement some methods for this custom type and this is how we would annotate lifetime parameters again same thing again same thing like in functions make it work by adding proper lifetime annotations you have a struct important excerpt and the port field holding a string slice now again string slice is a reference meaning we have here to annotate the lifetime parameter so that means the reference that the port field of destruct is holding outlives the entire struct right and over here we have a method implemented for the important excerpt struct so as you can see we are using a lifetime parameter here but we have to actually first declare it like that and that's basically it it should compile so when distract over here declares a lifetime parameter we have to declare it in the implementation block again but we are here not using it meaning we can do it like that right we don't care about this lifetime parameter here and this lifetime parameter is declared only on the on the method right this is compiling but there is an easier way actually we don't have to use any lifetime here right because we can Define here that the string slice is static meaning it will live throughout the entire program all right illusion some lifetime patterns are so common that the borrowed Checker will allow you to Omit them to Safe typing and to improve readability this is known as illusion illusion exists in Rust only because these patterns are common for a more comprehensive understanding of a lesion please see lifetime illusion in the official book so I have prepared some slides let's see there are three rules of Lifetime illusion the first rule states compiler assigns a lifetime parameter to each parameter that's a reference second if there is exactly one input lifetime parameter that lifetime is assigned to all output lifetime parameters third rule if there are multiple lifetime parameters but one of them is a reference to self or immutable reference to self the lifetime of self is assigned to all output lifetime parameters so let's see an example and before we see the example as you can see lifetime illusion makes writing rust much more easy until this point we didn't worry and care about lifetimes and that's most of the time the compiler just inferred it so we didn't have to worry now let's see here we have the function first word taking a reference okay and returning a reference so the compiler applies the first rule each parameter gets its own lifetime so the compiler behind the scenes will annotate here a lifetime for this function and we'll annotate it for all the references right so the second rule applies because there is exactly one input lifetime again we have only one argument that is a reference so this gets assigned to the output lifetime meaning the output reference here must then be the same reference right we are taking a reference so it must be the same when we are when we are returning a reference so in this case the compiler could infer the lifetime and we don't have to specify them manually so what the compiler will do he will just annotate the same lifetime as for the input argument right so this is all inferred you can write it like that and it will compile you don't have to worry about lifetimes so let's see this example we have the function longest that takes two references as arguments okay and it returns one reference first rule each parameter gets its own lifetime so the compiler will come and annotate A and B each reference gets its own lifetime all right so here the second rule doesn't apply because there is more than one input lifetime also the third rule doesn't apply because this function is not a method we have now to manually annotate the lifetime parameters so as you can see over here the compiler cannot infer The Returned reference here is it of Lifetime a or is it of Lifetime B right so it depends if you return X then you would annotate a lifetime of a if you return B then you would return then you would annotate a lifetime of B and again both lifet times A and B must outlive this function let's see an example in a method so as you can see we have a method taking a reference to self meaning taking the instance as a reference and doesn't take ownership so here the third rule applies which states that if there is a reference to self then all references will have the same lifetime as self right so we can have here any number of arguments all of them will take the exact same lifetime as the lifetime of self because this is inferred and it makes sense right because all of the provided arguments to a method must actually outlive the structure or the enum instance all right so all of the arguments here take implicitly the same lifetime as self all right so let's see remove all the lifetimes that can be elided as you can see over here we have only one input argument and the second thing is we don't even return anything so we don't even have to care about uh the lifetime because we don't worry that it might go out of scope right but in this case we only have one argument meaning the compiler is able to infer that over here again we have only one argument but as you can see the difference is we are returning a reference right so because there is only one argument here the compiler can infer that the reference over here must be the reference that will be returned so we don't have to annotate the lifetimes so let's see over here we have the longest function and the first rule says that the compiler will annotate a lifetime for each reference meaning it will create a and b because we have here two references right two arguments and it will annotate for the first argument a and for the second argument lifetime of B now over here this the compiler cannot infer which lifetime will get returned meaning as you can see we are returning X so the lifetime over here would be a right the lifetime of the argument X but notice both of the references must outlive the function right here we have a struct owner that holds an i32 type annotate lifetimes as in a standalone function so as you can see we have here methods implemented on the owner struct now again when we have a mutable reference or an immutable reference to self then we never have to worry about lifetime annotations because this will get inferred by the compiler all right all references pass to these methods will get the same lifetime as self or SD reference to self now over here we have to annotate the lifetime because this is a struct and we want to ensure that the reference the name field is holding is actually living longer than the instance of a struct person okay and over here the same thing let's see if this is compiling and this is compiling what we could do also here we could remove that and doing it like this all right this should actually also work okay we have here to specify that this string is static in this case we have to explicitly state it and as you can see this is compiling so you don't have to declare here a lifetime parameter you can put here this static Lifetime and static lifetimes will be our next topic let's take a look at static lifetimes so aesthetic lifetime refers to a lifetime that lasts for the entire duration of the program's execution any reference or borrowed value with a static lifetime can be safely used throughout the program and aesthetic lifetime can be coerced to a shorter lifetime if needed so we have seen string literals and they are usually annotated using an ampersand and stir but implicitly it would have a static lifetime so string literals have a static lifetime because they are hard coded into the executable meaning they are valid throughout the entire duration of the program's execution let's see static is a reserved lifetime name you might have encountered it several times so a reference with static lifetime is for example a string literal and this would be the full type annotation static as part of a trade bound so as you can see we are here defining a generic type parameter and we say that the argument provided should be of type T now in the Vera Clause over here we see that t must be of static lifetime though they are all static but subtly different as a reference lifetime Ampersand static indicates the data pointed to by the reference lives as long as the running program but it can still be close to a shorter lifetime there are several ways to make a variable with static lifetime two of them are stored in the read-only memory of the binary fill in the blank in two ways so as you can see we are calling here neat static within argument of V so let's declare that here and as you can see the argument must be of type static string or string literal all right and then we are here checking is the argument equal hello so let's initialize it here so the first way is just initializing it like that without any type annotation because this is simply inferred by the compiler so any string literal is hard coded into the binary means it lives as long as the program's execution Because the actual data of the string literal is living in the binary the second way is just annotating the full type annotation like that and this static lifetime annotation here can also be omitted so we can annotate it like this as you can see the compiler is able to infer the lifetime another way to make static lifetime is using box leak now this is an unsafe method and I won't cover that because it's a more advanced topic but I have solved it and will put it on my GitHub if you want to solve it for yourself static only indicates that the data can live forever not the reference the letter one the letter one will be constrained by its scope so let's see make a string literal and print it so over here we have a string literal meaning it's a string slice of static lifetime then we print it out when static string goes out of scope the reference can no longer be used but the data remains in the binary so at the end of this scope over here this variable will go out of scope meaning it's dropped and that means we can't then use static string anymore because the variable has been dropped now even though the variable now even though the variable has been dropped the data over here is actually still living on right so this is living in the binary a string literal is hard coded into the binary meaning it is of static lifetime meaning it is valid throughout the entire Pro programs lifetime but of course a variable is bound to its scope so to make that work we can take that and copy it over here meaning the variable static string is valid until main ends as you can see static can be close to a shorter lifetime so over here make a constants with static lifetime so this over here is the static keyword meaning we are here creating a constant now constants by conventions are always uppercase right and by the way the difference between conist the difference between this and this now both of them live for the entire lifetime of the program both are of static lifetime but the difference is that static will always remain at the same memory location while a const constant will get inlined so when we use for example num const here then it will get inlined meaning the compiler will basically copy that into the code in the function so the memory location could change but static always stays at the same memory location so returns a reference to num there is static lifetime is coerced to that of the input argument so as you can see over here we have a function corostatic and we have defined a lifetime parameter a we Define here that the input reference should be of this lifetime a right meaning the reference we take as an argument to this function must outlive the function itself now we over here actually return this constant right basically a reference to this constant and we can in this case course the static lifetime of this num constant to a shorter lifetime it's no problem right so a is shorter than static right aesthetic lifetime is larger than this a lifetime here meaning aesthetic meaning a reference with a static lifetime lives longer than a because a only defines that it must outlive this function right but we can hear chorus it to a shorter lifetime so it matches with the lifetime annotated on the function make an integer to use for coreostatic so over here we make an integer and then we pass a reference of this integer and called the chorus static function meaning we here returned a reference to an i32 right basically a reference to the number 18. and then we will just print out the result of this function call right now as you can see we have crossed num here to a shorter lifetime but this doesn't change the actual lifetime of this static constant right so even after this scope over here we can still access num because again it's of static lifetime it lives as long as the program is running so even though we have here corrected to a shorter lifetime it doesn't matter it remains static right let's see as a trade bound it means the type does not contain any non-static references for example the receiver can hold on to the type for as long as they want and it will never become invalid until they drop it it's important to understand this means that any owned data always passes a static lifetime bound but the reference to that owned data generally does not so so let's look at function printed as you can see we Define here a generic type parameter meaning the input meaning the input argument here must be of type T now this over here is a trade bound meaning we say the type T must implement the debug trade and T cannot contain any reference that is not of static lifetimes so all references T is containing must be of static lifetime right and over here we do basically the exact same thing but using the impul keyword right this and this is actually quite the same and then over here as you can see 22 takes a reference to T right and again T must implement the debug trade and it cannot contain any non-static references all right so I is owned and contains no references thus it's static as you can see we are here initializing I with a value of 5 meaning I is the owner of 5. now when we pass the owner to a function then it would actually comply with this function signature because owned types are also considered static okay now this would actually work because again i32 implements debug and it is considered static because we are passing an owned type right oops reference to I only has the lifetime defined by the scope of main so it's not static right so the so a reference to I has a lifetime that is defined by this main scope so it's not static anymore right so this would actually output and error right and this would be the same thing again print it and print it one would actually be very similar right now this here actually works because we are here passing a reference to I again right so we pass a reference to I and again the reference to I would have a lifetime of this main function now the difference is here that in the argument we defined that the argument should be a reference to T meaning if we think away it is ampersands I would be T right so a reference to T would be a reference to I hope this makes sense so that means again when we pass I directly it would be it would Implement debug and it would be of static Lifetime right so this over here would work now to fix this over here we can do it two ways we can create here the constant I and again constants have a static lifetime as you can see this is compiling and the second thing is we can use this static keyword meaning I is static so this would pass because the argument we are passing or the references we are passing are all of static Lifetime and they implement the debug trade right i32 implements debug and we have ensured that all of them are of static lifetime now the last one here again deals with box leak I won't cover this but again the solution is in my GitHub if you want to solve it you can do that and then compare with my Solutions alright see you in the next one so let's see closures a closure is an anonymous function that is able to capture the values from the scope in which it is defined and if you are coming from python then you might know Lambda functions and in JavaScript they are called Arrow functions and they can be defined inline for example as a function parameter they don't require type annotations and they can take ownership of a value by using the move keyword so all functions and closures implement the F and trades and that is the trait that defines the signature for closures and functions it describes the types the number of arguments and the return type there are three different traits FN ones the closure can be called only once because it takes ownership of the captured values FM mute it might mutate a captured value it can be called more than once and FN it doesn't take any ownership of captured values it doesn't mutate anything and it might not even capture anything from its environment so let's take a look over here we have here initialize the variable x with a value of 1. now over here we would have a closure and closures are defined like that first of all we have these symbols over here and between them we Define the name of the argument and as you can see over here we just add the argument with the value of x so as you can see the closure here is able to capture the value of x something a function can't do right and we then assign this closure to a variable meaning we then can call this closure and calling it like in normal function so in this case as you can see we call the closure using the variable name closure and then providing it an argument right so actually disclosure call should evaluate to 3 because we provide 2 here meaning weld will be 2 and adding X to it one so two plus one will be 3. so disclosure captures the value of x and modifies it the compiler will capture variables in the least restrictive manner possible in this case immutable reference of X is taken rather than taking ownership because it's less restrictive let's see closures can capture the enclosing environments for example we can capture the X variable that's the example we have seen from the syntax we can see that closures are very convenient for on the Fly usage unlike functions both the input and return types of a closure can be inferred by the compiler so we have here a normal function as you can see it takes an argument of type i32 and it will just add 1 to T and it will just add 1 to the provided argument and returns it meaning the return type will be i32 now closures are Anonymous here we are binding them to references these nameless functions are assigned to appropriately named variables so as you can see we can here create a closure and this and this over here are exactly the same so type annotations enclosures are not required okay and then again we assign the closure to a variable in order so we can call it all right over here we have a variable I with a value of 1. and then we are just calling the function and these two closures with this value over here and a closure taking no arguments which returns an i32 the return type is in third so this would be a very primitive closure just with running one when being called so when we call one here we will just get back one let's execute that you can see when we call this function and these two closures it will just add one to the provided argument and over here we are just returning one capturing closures can capture variables by borrowing or moving but they prefer to capture by borrowing and only go lower when required so they can capture the values either by reference by mutable reference or by value meaning the closure will then only type it captures okay so let's see exercise one make it work with least amount of changes so over here we are initializing a variable color with a string so as you can see over here we have a closure and we assign it to print so we can then call it now this closure here doesn't expect any arguments but it uses the move keyword meaning the captured values will be moved inside this closure right so that means the closure here will become the owner of the captured value this is not always the case so for example over here a immutable reference is sufficient because we are just reading basically this data here we don't mutate anything and we don't need ownership to print something out but as you can see printing two time over here will cause an error right so what we can do is we can remove the move keyword and color can be borrowed immutably again because the closure only holds an immutable reference to color right that means when we don't move the captured value we can then take a reference to color again so this foothold a reference to a string as you can see we print here out two times color green because we are calling this closure two times and this will take an immutable reference of this captured value all right and then we can take another reference of this string so let's see exercise 2 make it work don't use reborrow and count reborrowed don't modify assert EQ so we have over here an i32 by the way this is mutable and we have here a closure taking no arguments then as you can see it captures this variable here all right and it mutates it meaning count will then hold one and then we just print out count over here we are calling disclosure and then we are taking a reference to count meaning a reference to an i32 right and then we are again calling this ink closure the closure no longer needs to borrow mutable borrow account therefore it is possible to re-borrow without an error so what is happening over here we have seen that the compiler will take the least restrictive approach so in this case it will take an so in this case the count variable here will be captured as a mutable reference right and that would be a problem because as you can see we are calling Inc and over here we call it again after you're defining an immutable reference to count so maybe you remember that you can either have one mutable reference or any number of immutable references but not both at the same time so this would be a problem so we can call here the move keyword meaning disclosure here takes ownership of this value now again because i32 implements the copy trade it means it will actually get a copy of count so count is still accessible even after this closure call right the closure will just take a copy of the value and that means we can then take an immutable reference again to count and call the closure right and over here the closure no longer needs to borrow a mutual reference to count therefore it is possible to re-borrow without an error so as you can see we are here taking a mutable reference to count again because we don't use this immutable borrow over here again after this point right so this is allowed let's see and as you can see this is compiling and here you can see that count has been modified so let's see exercise 3 make it work in two ways none of them is to remove take movable away from the code so as you can see we have here initialized a variable with a heap allocated i32 integer right then over here we have a closure and we capture here this movable variable now we have seen that actually the compiler will use the least restrictive approach so in this case because we have only a print line it will take this variable here as immutable reference we just need to print it out now over here we are calling the take function as you can see the take function takes an argument of any type but it takes ownership meaning when we call the take function with an argument the argument will then lose its ownership so take function will become the owner of movable in this case right and that means we can't call disclosure two times because in this case again the compiler will try to use the least restrictive approach but in this case it's necessary that this closure over here will Implement F and once so it only can be called one time because again over here move a bell when we call this take function it will take movable and take ownership of it because it's necessary for the compiler to do that to comply with this function signature but what we can do is we say we want a reference right so again the compiler will then implement the F and trade for this for disclosure meaning it can be called two times now we will see a lot of more example of these FN trades so don't worry it just means now because we just need over here immutable references the compiler will actually capture this variable here as an immutable reference right so nothing gets moved inside this closure and movable will remain the owner of the data for comparison the following code has no error foreign so as you can see we have again a heap allocated integer and we have here a closure now as you can see we use the move keyword here and in this case disclosure will capture the movable variable only as an immutable reference because we here only print out the variable we don't need an we don't need a mutual reference and we don't need to take ownership so we can call this closure two times without any problem type in third the following foreclosures has no difference in input and return types so this is actually a function not a closure but as you can see all of them are exactly the same so we can skip the type annotations and we can even skip the curly braces if it is declared on the same line so this over here is exactly the same as writing it like that so over here we have an example closure taking one argument and just returning that argument so then V over here call the example closure with a string meaning over here this would be the argument provided and it returns a string now when you define a closure and call it with a concrete value the compiler will then infer the types of the arguments and the return type so in this case the compiler will annotate the types like that right we take an argument of type string and return the string and that means that we then can't call the closure again with another type this would cause an error okay what we have to do is to convert that to a string let's do it like that so we get back a string here and this is compiling so let's see FN F and mute FN once when taking a closure as an input parameter the closures complete type must be annotated using one of the following trades FN the closure uses the captured value by reference F and mute the closure uses the captured value by mutable reference F and once the closure uses the captured value by value basically taking ownership alright so make it work by changing the trade Bound in two ways so when a function accepts a closure as an argument we have to actually Define what will be the type of the arguments to the closure and the return type of the closure right and we over here can see that a closure implements one of these three trades all right now over here in the fn1's function we Define that the fund argument must be of type f and here we Define the trade bound so F must be a type that implements F and ones taking as argument U size and returning a Boolean value all right and inside this function over here we are just calling the provided func closure and providing its arguments so as you can see we have here a vector and we call the FN ones function with an argument of a closure as you can see we can't pass a closure as a function argument now in this case disclosure will implement the fn1 straight right basically meaning it will take ownership of the captured values and it can be only called once so it takes as argument a a type of view size right so and this is necessary here because we are comparing the provided argument 3 and 4 with the return value of the Len method right this land method is defined on vectors and it will return EU size so that over here has to be of hue size we have to Define that and all this does is we can provide disclosure an argument and it will check is for example 3 over here is it the same as the length of this vector now the problem here is that we have to change the trade bound because at the moment we are calling the funk closure passed as argument to this function two times while it implements FN once meaning it can only be called once now what we can do over here is we take FN right basically just taking an immutable reference of a captured value now the captured value in this closure here would be X right this vector acoustic because the closure captures it from the environment it is defined in and this should actually work let's see as you can see when we pass this closure to this function and we Define here the closure must implement the FN trade taking a u sizes argument and returning a bull then we are calling the closure right Funk here holds a closure so we are calling it with three in this case this would evaluate to true because the length of vac is three elements right so x dot length will return three meaning it's equal to the provided argument and four of course would return false so we are here initializing a string and over here we are defining a closure it takes an argument and it then modifies the string over here basically pushing the string that was provided in the argument then we are calling the exact function providing it this closure all right then let's see all this function does it takes a closure as an argument and then it will call the closure and it will pass it a string literal right meaning that to this string as this string literal will be pushed right so after calling this closure s should then hold hello all right and then we just print out s now what would be the appropriate trade Bound for this closure think about it we have FN FN mute and FN once so what would be the least restrictive approach it would be FN mute right because we are mutating here this string the captured value but we don't need to take ownership in order to be able to mutate it right now we have to provide the argument this would be of type string slice right we are passing here at this string literal which is string slice and then we have to provide a return type so actually this won't return anything right so we don't here have to provide a return type so let's see if this works and as you can see we have here a lifetime parameter and this is just ensuring that the past string literal here is actually outliving this function exact right which is the case because string literals remember are static lifetime they live the entire duration of the program and as you can see when we print out s it has successfully been modified so it's sufficient to use here F and mute but if we want we can even take ownership but then we have the Restriction that we can only call this closure one time right so if we try that as you can see this is working two which trade does the compiler prefer to use FN the closer uses the captured value by reference FM mute the closure uses the captured value by mutable reference fn1 stick closure uses the captured value by value on a variable by variable basis the compiler will capture variables in the least restrictive manner possible for instance consider a parameter annotated as fn1s to specify that the equation May capture T by reference by mutable reference or taking ownership but the compiler will ultimately choose based on how the captured values based on how the captured variables are used in the closure which trait to use is determined by what the closure does with captured value this is because if a move is possible then any of borrows should also be possible note that the reverse is not true if the parameter is annotated as FN then capturing variables by Mutual reference or by T is not allowed let's see number seven fill in the blank a function which takes a closure as an argument and calls it f denotes that f is a generic type parameter so as you can see we have here the function apply and it takes as an argument a closure and all it does it will then call disclosure right and we have here to define the trade Bound for the provided closure a function which takes a closure and returns an i32 so over here we have applied to three again taking a closure and we have 10 to Define here the trade bound the closure takes in i32 and returns an i32 and again this function over here will just call the closure and the difference is it will then return The Returned value from the closure so as you can see over here it just calls the closure but it doesn't return anything because here this is a statement it's ending with semicolon here we omit the semicolon meaning the return value of the closure will be the return value the function returns all right so over here we have a string knittering in non-copy type to own creates owned data from borrowed one so as you can see we take a string literal and call the two owned method now the UN now the two ohms method will convert a string slice to a string capture two variables creating by reference and Farwell by value so over here we have a closure then greetings by reference requires FN so as you can see this closure captures the greeting variable now all it does it will print it out meaning to take a an immutable reference would be sufficient so the compiler will implement the FN trade for disclosure because it is sufficient to only take an immutable reference to the captured variable all right mutation forces farewell to be captured by mutable reference now requires FN mute so as you can see we are taking Farwell and we are modifying it now this requires far well over here to to capture as immutable reference again you can see that the compiler tries always to use the list restrictive approach so then the compiler will instead implement the FN mute trade for disclosure all right because again we capture here a variable which needs to be mutated then manually calling drop forces for well to be captured by value now requires FN once so this mem drop here allows that we manually dropped a value from memory right but this actually requires that we take farewell as a value right so the closure should take ownership of this variable meaning the compiler will now implement the fn1 straight for this closure right so as you can see step by step here an immutable reference was sufficient so it just implements the FN trade here a string got mutated so it takes the captured values as f as IMM mutable reference meaning F and mute is implemented and here when a function needs to take ownership then of course the captured value must be captured as owned type meaning fn1s is implemented so that means if this closure implements the fn1 straight it can only be called once because you have inside values that capture the environment values by taking ownership Quality Function which applies to closure so we call the apply function here providing it an argument diary disclosure here right we are calling apply and providing diary now let's see this would be this function and as we've seen because Farwell here gets captured by by value meaning the closure takes ownership we have to Define here that it will be a closure that implements the FN once trait right now as you can see the closure doesn't take any argument and it doesn't return anything so we would annotate it like that so again this just defines that the generic type f must implement the FN once trade and over here double satisfies apply to three straight bound so here we have a closure taking one argument and it Returns the result of this operation right so we call the apply to three function providing it as argument disclosure and as you can see over here apply to 3 is a closure generic type f and V here Define that the type f must implement the FN trade and as you can see it takes as an argument an i32 and the return value and the return type will be i32 so let's see I take disclosure here and disclosure was passed as an argument to this function and all this function does is it will call the closure with an argument of 3. now X here will be replaced by three and the return will be 2 times 3 which would be 6 right so disclosure then returns an i32 namely 6 and as you can see because we omit here this semicolon it means that the return value of this closure will be the return value of this function so when we call this function over here the return value over here will be 6 as you can see this is all compiling move closures May still Implement FN or FN mute even though they capture variables by move this is because the trades implemented by closure type are determined by what the closure does with the captured value not how it captures them the move keyword only specifies the letter so the move keyword only specifies how the captured values are captured while the FN trades determine what the closure does with the captured values so as you can see we have here a string and we have here a closure with the move keyword meaning values will get moved inside disclosure now because over here as you can see as in this case doesn't need to be taken as value so the closure doesn't need to take ownership of the data because it will just print it out right so an immutable reference would be sufficient and that's exactly what this says so over here as you can see we are calling the exec function with this closure and when we see over here the exact function takes us an argument a closure that implements the FN ones trade all right so it will in fact take ownership of the captured value and then in the function itself it will just call the closure and Returns the return type of the closure now over here as you can see again we have a string and we have a closure even though we have the move keyword as you can see we Define here that the provided closure to exec only has to implement TF and trade so the string s here will get moved so basically this closure here takes ownership but we can still Define disclosure over here to just Implement FN because again even though we are taking ownership we are just printing out right the difference between move and the event rates is the event rates Define what the closure does with the captured values and move defines how it captures them so it would capture here by taking ownership but the FN trait only defines what are we doing with the captured value in this case you're just printing it out meaning an immutable reference would be sufficient so we can even here pass FN and it still would work fill in a blank so over here we have a string and this would be a closure that takes an argument and returns a string now what we do over here is pushing to this string the provided argument right and then what we do is we will return s meaning the string right so when we call over here this function and provided the closure then we have to implement the FN once rate and it will take as an argument a string literally let's annotate that with the lifetime parameter a and it will return a string right so this would be so this would be the trade bound we are defining here now why did I Implement here FN once or why did I Define that F over here the type of the argument must implement the fn1 straight because as you can see we are capturing s and modify it now over here it would be sufficient to use an FN mute right now again it would be sufficient to just use a mutable reference to S but again we are here returning as right we are returning this captured variable meaning we must be the owner to return something so we have to Define that F should implement the fn1 straight right because we are here returning s meaning the closure must be the owner in order to return it let's see and this is compiling input functions since closure can be used as arguments you might wonder can we use functions as arguments too and indeed we can so as you can see we have here the call me function Implement call me to make it work and over here we have a normal function just printing out something and in main here we have a closure right also just printing out something then we call the call me function here two times one time with a closure and one time with a function and that means let's see first of all we Define we want here A type f which is generic okay so we have to Define here a generic type parameter and then of course we have to define a trade bound right so in this case over here we are not mutating anything and we don't need to take ownership and in fact over here we don't even capture any variables right it will just print out something and it doesn't capture anything so in this case FN so in this case the F and rate is sufficient right and by the way they don't take any argument and they don't return anything so let's see and this will be the output so we call call me providing it to closure the closure implements CFN trade and then call me we'll just call the provided function and closure right meaning when they are called they will just print out this over here course closure first because closure has been called first let's see exercise 10 closure as return types returning a closure is much harder than you may have thought of now it's actually not that hard but you will see fill in the blank using two approaches and fix the error so as you can see we have here the create FN function and we have to Define here the return type so let's first see we have over here a variable that is declared inside the function meaning num will only live as long as this function lives right and then over here we are returning a closure notice we omit here semicolon meaning this whole closure will actually be returned by the function and when we go into main as you can see we call the create FN function then this function should return a closure meaning F and plane will hold a closure basically F and claim will look something like that right holding a closure meaning we then can call the FN plane closure with a integer all right because we have to Define here that X should be of type i32 because we here capture num which is of type i32 remember when we perform an operation both values must be of the same type now let's see we call the create FN function then we initialize the num variable and then we have here the closure that will get returned meaning num here will capture this variable and when we then call the closure over here that will get returned by create FN then we provided a value of 1 meaning X will be 1 and num will be 5. right meaning the value the output should be 16. when calling let's write it here after calling the closure itself the output should be 6. now let's see first of all we have to think about how is this num variable captured now again the compiler will use the list restrictive approach so it will just take a reference problem here is that we actually return this closure right so num actually must outlive this function otherwise when we return the closure and num gets out of scope then over here we hold a closure that refers to something that got out of scope so let's use the move keyword here meaning num gets moved into the closure so the closure will become the owner of this variable all right now again here we can use static dispatch or dynamic dispatch now I will go first with static dispatch so in this case we want to return a type that implements the FN trait right because over here we have the closure that we want to return so let's Implement that disclosure should implement the FN trait and again over here FN is sufficient because even though we have captured the num by ownership the actual closure only performs an operation so it doesn't modify num and remember move defines how we capture the closure and the FN trades Define what the closure will do with the captured value alright so FN here is sufficient and disclosure takes as an argument in i32 right because the argument and the num variable must match because we are performing an operation on them and the return type should also be i32 all right the result of this operation so let's see and this is compiling now let's actually see the output as you can see the output will be 6. now this is using static dispatch now we can also use Dynamic dispatch remember when you when dealing with trade bounds like this we then have to basically box this over here and we put this over here into a box right this would then be a trade object we are using Dynamic dispatch here so let's see and this is also working so we have implemented the return type in two approaches fill in the blank and fix the error so as you can see we have the function Factory taking one argument of type i32 over here we have a variable of type i32 and if x the provided argument is bigger than 1 then this will be returned and else if it is smaller than one disclosure will be returned so using here the static dispatch approach doesn't work okay we cannot do something like that because over here we don't know exactly which closure gets returned and as you've noticed they are identical right but in fact disclosure is different than this because these have been both declared and they are located in different memory locations all right so even though they are exactly the same the compiler can't know at compile time which of these closures will get returned because they live at different memory locations now what we can do over here is using the dynamic dispatch approach so for that again we are boxing these closures and we Define here that the return type will be a box that holds a trait object namely a trade object that implements the FN trait taking as argument and i32 this x here and returning an i32 right let's see right and we have to provide here the main function because otherwise the program won't compiled and this is compiling alright see you in the next topic so iterators will be the last topic we will cover together so let's Dive In iterator allows to perform a task on a sequence of items in turn iterators are lazy meaning they have no effect until methods are called that consume the iterator to use it up all iterators implement the iterator trade which provides the next method which gets called automatically when traversing over some data and some methods consume the iterator While others produce a new iterator from the provided iterator let's see the iterator pattern allows us to perform some tasks on a sequence of items in turn an iterator is responsible for the logic of iterating over each item and determining when the sequence has finished so over here we have a vector and then we are iterating over the elements in this vector and as you can see the variable X will hold in each iteration one element of the vector so when we print that out as you can see in each iteration it will just print out X which means X in the first iteration is 1 then 2 and then 3. in the code buff you may consider for as a simple Loop but actually it is iterating over an iterator by default the for Loop will apply the into ether to the collection and change it into an iterator as a result the following code is equivalent to the previous one so this is happening implicitly that V over here this vector or really any collection will be called with this into either method meaning this collection will be converted to an iterator right and then we Traverse over each item so let's see exercise one refactoring the following code using iterators so we have here an array holding 10 Elements which are all zero something like that okay 10 times then we have here a for loop iterating from 0 to array length where the actual length is excluded meaning from 0 to 9 right 10 times and then we just index into this array and print out the and print out the element living at the specific index so when we execute that as you can see we print out 10 times 0. now again we don't have to use this syntax we can just pass here the array itself and it will do exactly the same and we have seen that the compiler implicitly will put that into an iterator like that and as you can see this would be the exactly same thing one of the easiest ways to create an iterator is to use the range notation so over here we are initializing an empty vector right then we want to iterate here over a range and in each iteration we are just pushing n to the vector now in the end the length of this Vector should contain 100 elements right so what we can do over here is going from 0 to 100 meaning 100 is excluded going from 0 to 99 meaning the vector will then hold elements from 0 to 99. like that right 100 elements and this will compile next Method All iterators Implement a trait named iterator that is defined in the standard Library as you can see this is the iterator trade that is defined in the standard Library it has an Associated type item which basically refers to the type we are iterating over for example a vector of u8 then the item type will be u8 right and as you can see each type that implements the iterator trade will need to will implement the next method taking a mutable reference to the instance and returning an option containing the type of the associated type defined here and we can call the next method on iterators directly so over here we have a vector and we can then put that into an iterator right so this will then actually hold an iterator right and not a vector anymore so we put this collection into an iterator meaning we then can use the next method that is implemented for the iterator type and that is actually exactly what is happening when you are doing a for Loop for example in the back the rascompiler will just call next all the time until it reaches a point where there is a non-return because remember this next method will return an option type so when we put that into an iterator and call next we will get a sum and the first element right then sum and two and as you can see we don't have any more elements so it will return Norm right this is the option type and we have covered that and when we put a collection into an iterator it should be mutable right so the next method takes a mutable reference we have seen so we have to make it mutable all right into iter eater and iter mute in the previous section we have mentioned that 4 will apply the into ether to the collection and change it into a iterator however this is not the only way to convert collections into iterators into either eater and intermute all of them can convert a collection into an iterator but in different ways so into ether consumes The Collection once the collection has been consumed it is no longer available for reuse because its ownership has been moved within the loop ether dispers each element of the collection through each iteration thus leaving the collection untouched and available for reuse after the loop and it remute this mutably borrows each element of the collection allowing for the collection to be modified in place so as you can see we have here a vector then we Loop over this Vector here right and all we do here is actually printing out each of the elements now we have seen that implicitly the compiler will call here into iter meaning the iterator will become the owner of this data right so we can't reuse these are after we have put it into this iterator but there is not a solution we can just use either here because it's sufficient that we have a mutable because it's sufficient here that we have an immutable reference right we just want to print it out we don't need to mutate anything or anything else we only need read only data right so let's see and as you can see we then iterate over each element printing it this over here and then we can reuse the collection over here so ownership hasn't been transferred into this iterator five fill in the blank so over here we have a vector with string literals okay and then over here we are iterating over the elements in this vector now as you can see we are here matching the element and if it is immutable reference to the string literal fairies then we will actually modify the elements that we are iterating at this exact moment to this over here if it is anything else then it will be replaced by hello right so in the first iteration name will hold a string literal this over here then we match this and in this case this arm will match meaning we assign string literal hello to this element right so instead of Pop we would then have hello in the first index right so as you can see we are mutating meaning we can here use either mute right we want to take mutable references from this vector but as you can see over here we are again using these names Vector meaning we can't take ownership so let's see and this is compiling and as you can see the first two elements got replaced by hello and the last one ferries has been matched here right name holds a mutable reference to the string slice Ferries and that's why this will get returned meaning it will be assigned to this element let's see exercise 6 fill in the blank as you can see we have here a vector now over here we want to put this Vector into an iterator right now before we do that as you can see this is the vector in the beginning and this is how it should look like in the end so we want to mutate the first element so in this case we take it as mutable references calling The Ether mute method right so we take from here mutable references of the elements in this vector then over here we can call the next method which will then give us back an option type of the first element right so we pet our match here some with the inner value and then what we can do because we get here a new to the reference meaning we can dereference it and assign it a new value like that so we get no output let's actually see the elements as you can see we changed the first element we have mutated it using ether mute right getting bad getting back immutable reference to the elements creating our own iterator we can not only create iterators from collections type but also can create iterators by implementing the iterator trade on our own types so as you can see we have here a custom type counter which has one field and we Implement here the associated function new basically just creating an instance and then over here we implement the iterator trade for counter right so first of all we have to give the associated type a concrete type in this case u32 right basically the type this field is holding then we implement the next method as you can see next takes a mutable reference to the instance and it returns an option that holds a type of the associated item here right it would be u32 in this case so if self.count is less than 5 then we increment the count field here and we return the actual count otherwise we return none so if self to account is bigger than 5 then it returns none so as you can see we are then creating a new instance of this counter type using the new Associated function and then as you can see when we call the next method on it right because we can do that because we have implemented the iterator trade for our custom type so when we do that we just get back T value of count as you can see when calling next then over here it checks is the self to count field less than five in this case it is right we didn't increment it so far so it should hold zero and in that case it will increment it by one right the count field over here gets incremented by one and then it returns it and of course packing it into a sum because we return here an option type so that means we get one in the next iteration we get 2 3 4 5 and then as you can see the count field holds a value of 5 meaning this won't get meaning this would evaluate to false and this will get executed right so we will get back a non and over here we have Fibonacci so the Fibonacci sequence starts at zero then one then zero plus one is one then one plus one is two then one plus two is three two plus three is five five plus three is eight and so on right and as you can see we have here distract Fibonacci that has a current field and a next field so as you can see current for example would hold zero and next would hold one and as you can see we are implementing the iterator for Fibonacci so we Define here the return type to be u32 right or basically the type of the items we are iterating over so these both of these fields hold u32 and over here we have a function Fibonacci which just returns an instance of the Fibonacci struct with current holding 0 and next holding one so notice something here when we call next we actually want to return someone and not some zero so this is important but let's now implement the next method so we have seen that it will return option type holding self item write the type that is defined in the associated type item and then over here I will create a new variable forward and this would then be self dot current plus cells dot next right because so forward in this example would be zero plus one right this would equal one so we can then to go one step forward we want to go like this and then like this right we can assign to self current self next meaning in the next iteration the current field will hold one right and self.next will hold forward right self to current plus self dot next meaning in the next iteration next we'll hold one so we will be at this point right and then all we need to do is we wrap it in sum because again we return an option and we return the current a field over here let's see methods that consume the iterator the iterator trade has a number of methods with default implementations provided by the standard Library consuming adapters some of these methods called the method next we use up the iterator so they are called consuming adapters so over here we have a vector and we put that into an iter notice we take here the iter method meaning it will just take immutable references to the elements and then we call on this iterator this sum method the sum method will take the ownership of the iterator and iterates through the items by repeatedly calling next method right so all this sum method does it will just add together all the elements in the vector notice sum will take ownership of this iterator meaning we can't use V1 eater again right so in this case the total will be six now I can't edit here let's see it in the playground so we have here a vector and then we put it into an iterator and then we call the sum method on the iterator meaning this should then hold i32 and the value should be 6 right just adding all of these together now that means we can't use V1 either again here this is not possible because it is some method will take ownership of the V1 eater variable let's see as you can see this is compiling let's actually print out the result as you can see we would have six collect other than converting a collection into an iterator we can also collect the result values into a collection collect will consume the iterator so over here we have an array which holds two tuples right and each Tuple has as and each Tuple has a string literal and an i32 right then we take this names array here put it into an iterator notice here into either meaning we take ownership of these elements and then we collect meaning we collect it into another collection which has to be annotated here meaning we will then create a hash map with string literals as keys and i32 values right then we print it out here and over here we have a vector of i32 types and we call The Ether method over here meaning we just take references to this vector and then over here we call collect meaning it will get collected into an iterator right because we want here V2 meaning and then we annotate here that we want a vector of i32 because as you can see V2 should hold a vector with these elements let's see and actually over here we would have references of i32s right because again ether here takes references of these elements meaning they would not equate we want a vector of I 32 elements meaning we can call the input ether and that means ownership of these elements will be transferred to V2 and this would be the output of the print line over here we are printing here out the hash map we have converted iterator adapters methods allowing you to change one iterator into another iterator are known as iterator adapters you can chain multiple iterator adapters to perform complex actions in a readable way but because all iterators are lazy you have to call one of the consuming adapters to get results from calls to iterator adapters so over here fill in the blanks we have a vector and we iterate over the elements just taking immutable references alright then over here you can see we want to modify the elements inside this Vector to look something like that basically just incrementing each element now we can use here map to do that right Now map will create another iterator it will take ownership of this iterator here and it will then create another iterator right but with the modified elements and then we can call here collect to actually put it into a collection meaning this Vector over here let's see all right this is compiling I guess this would be i32 so we have taken it from this vector so this is compiling let's actually output as you can see we have modified the elements but of course V1 is still accessible and that is because we have called here the eater method meaning we just take immutable references of the elements in this vector meaning the ownership will remain at V1 so we have now reached the end of this course and if you have made it this far then your dedication to this language is really impressive and this is only the beginning of your journey I hope this course was helpful don't forget to split the word about free software and I'm outwelcome to this rust programming course for beginners this comprehensive course will guide you step by step through the fundamentals of rust enabling you to build robust and efficient applications from understanding basic syntax and data types to exploring more advanced topics like lifetimes and closures arfan zubi will provide clear explanations and Hands-On exercises to ensure you grasp every concept effectively get ready to unlock the power of rust and elevate your programming skills to new heights hi my name is Irfan and I will be your guide in this comprehensive for us course for beginners so if you're interested in learning rust then congratulations you have found the right course so I've designed this course to ensure that you will learn rust the proper way using slides and visuals to illustrate Concepts and providing exercises we will solve together this means you will have an appropriate mix of theory and practice that will help you step by step mastering this new awesome programming language and I strongly advise you to code along because you will learn so much more when actively participating rather than just watching the video so let's Dive In so what are the goals of this course first and foremost getting familiar with Core Concepts and syntax of the language we will take a look at data types and data structures as well as ownership and borrowing we will see how data is allocated in memory and how we access the data and we'll take a look at standard Library generics trades lifetimes and much much more basically everything you need to become a great rust developer so why would you want to learn rust now there would be I guess a thousand reasons but I have here put together some of the main ones now rust is a really fast language in terms of execution time it provides a rich type system it doesn't have a garbage collector which is part of the reason why it's so fast it provides useful compiler outputs and you will see that in the exercises we are solving it guarantees memory safety and it's the most beloved programming language since 2016 according to stack overflow meaning now seven years in a row and we are experiencing fast adoption in various branches foreign now over here you can see a table which shows various programming languages and their score in terms of Energy Efficiency and execution time and as you can see rust is one of the languages on the top of the list now over here are some resources that you can learn with now I guess the main resource would be the RAS programming language really fantastic book and I recommend you to read it then there is rustlings these are small exercises basically programs which you have to debug then there is Ross by example so if your style is more focusing code and less on text then this would be suitable for you and there is a website called rust by practice and that is what we will cover together and if you want to go more advanced there is a book called Russ forestations and it's also very much recommended to eat it so before we dive into the first topic I want to show you the website of lost by practice and you can access it by going to the URL practice dot RS and this is the website and over here as you can see these are all the topics we will cover together right now this will be the first one variables and as you can see over here we have small exercises that we will solve together now before we start I want to show you something else and of course we'll keep up with the tradition and write a hello world program so you can go to the rustlang.org website this is the official website of the rust foundation and you can click over here playground and as you can see over here you can write rascode and then execute it and it's really cool because you even have the Vim key bindings over here and you can even choose themes like that and let's write our first program so every program in Rust starts in the main function this is the starting point of execution of every rust program and we declare functions with the FN keyword so to print out to standard output we can use the print line macro as you can see we write print Ln exclamation mark and then we can provide here the string we want to Output like that and then you can hit run and as you can see we have executed our first program so congratulations I hope you coded along and now we start with the actual course so variables variables are assigned using the LED keyword so if you're coming from a JavaScript background this might seem familiar you can print to standard output by the print or print Ln macros so print and print Ln are basically very much the same but print Ln adds a new line at the end of the output and scope of a variable is defined by the block of code in which it is declared now a function is a named block of code that is reusable and shadowing allows a variable to be redeclared in the same scope with the same name so let's solve some exercises binding and mutability a variable can be used only if it has been initialized fix the arrow below with least amount of modification to the code so again we are starting always in the main function now over here we have two variables and again we declare variables with the let keyword then the name and over here we have a type annotation now don't worry too much for now we'll cover that in the next topic to come but for now think of i32 as an integer type right and then we have here the assert EQ macro which stands for assert equality so this macro takes two arguments and just basically asserts that these two are equal in case they are not equal the program will Panic meaning immediate exiting and returning an error message if there are in fact equal then the program will just continue executing so as you can see we want here that X is equal to a value of 5 so we can initialize X over here with with a value of 5 like that and to run this program you can either click here or you can press Ctrl enter and let's read that over here uninitialized but used error so when you use a variable it has to be initialized with a value which makes sense right you can't use something which isn't initialized yet now y over here is uninitialized but also unused this will only output a warning now over here you can see the warning actually but if I take this code and put it over here as you can see it gives us a warning unused variable y okay to fix that we can prepend it with an underscore like that all right use mute to Mark a variable as mutable so in Rust a variable in its nature is immutable and you have to explicitly state that you want a variable to be mutable so fill the blanks in the code to make it compile as you can see we are here using a variable X so we want over here to declare X and initialize it with a value of 1. now over here we are mutating X which means this variable X over here should be mutable and we do that using the mute keyword like that now this over here is shorthand Syntax for this all right so we take the value of one we add 2 to it and then we assign it back to X meaning X will then hold 3. right now just a quick note I will always try to annotate the type in this course most of the time it's not necessary so when you see me annotating types it's actually not because this is necessary but just because I want to make stuff as clear as possible so in this case this would hold an integer type and again we will cover that in the next topic let's see as you can see this is compiling X is equal to 3. scope a scope is the range within the program for which the item is valid fix the error below with least amount of modification so over here we are initializing the variable x with a value of 10. an integer type all right now now here we have another scope we also can call it the inner scope and this would be the outer scope right so in this scope over here we are declaring another variable y now we over here try to print out X and Y and we can do that by placing inside the print Ln macro these placeholders over here and then providing as additional arguments over here the variables we want to put the values inside here so for example if we provide X over here then this will get replaced by 10 because X is holding a value of 10. and as you can see in the outer scope we are doing exactly the same now this program won't compile and the reason is because a variable is only valid inside the scope in which it was declared so for example the variable Y is declared inside the scope meaning T variable Y is only valid until this point all right that means we can't use y here so what would be the solution solution would be very simple we just initialize the variable y in the main scope in the outer scope over here meaning X and Y both of them are valid until this point over here right because this over here is the main scope let's see as you can see this is compiling fix the error with the use of Define X now we can have other functions besides main so over here we have the function define X and as you can see we are initializing a variable X which holds a string now this would be T-Type annotation for a string right don't worry too much about the annotations now because we will cover that but just to let you know now the problem here is that in main we are trying to access X now in this scope over here there is no variable with the name of X which means that this program won't compile because the compiler will tell you I can't find over here a variable with the name of X so what we can do over here is actually taking this line and putting it inside the function over here because as you can see in this scope of the function a variable X has been declared and initialized but if I run this program now nothing will happen and that's because as I've said the starting point of every Ras program is in the main function now when we run the code nothing will get executed and that's because we have to actually call this function for it to do something so in this case we can call a function like that just providing the name of the function and these parentheses and as you can see the output will be hello world right because over here we are providing x to the print line macro and we are appending to it comma world like that shadowing you can declare a new variable with the same name as a previous variable here we can say the first one is shadowed by the second one only modify a sort EQ to make print Line work print 42 in terminal as you can see over here we are initializing the variable X which holds an i32 type an integer type now in this scope over here we are initializing another variable with the same name X but over here we are initializing it with a value of 12 instead of 5. and that means when we assert the value of x over here it should actually be 12 right now what would be the value of x in this outer scope if you set 5 then you're right because this variable over here has been declared in the main scope in this scope over here that means X is holding a value of 5. now in this scope over here x holds a value of 12. and these are basically two different worlds right so the variable X declared in the main scope doesn't actually care about the variable X declared in this scope over here they are separate now over here we are shadowing X in the main scope meaning we re-declare and re-initialize the variable X again with an integer type now when we output X it should actually print out 42. as you can see this will be the output number six remove a line in the code to make it compile now over here we have a mutable variable X which holds one an integer type then we are assigning 7 to X now X holds seven right then we are shadowing and rebinding so we are re-declaring X and re-initializing it and we initialize it with a value of x meaning with 7 right and then over here we are mutating X and that means we are here incrementing x by 3. again using this front end notation which would equate to that and this would actually fail and I want you to look at it and think about it what over here might cause an error so over here as you can see we are re-declaring X and over here we are mutating X now as I've said every variable in Rust is in its nature immutable meaning if we redeclare X like that X will be immutable even if it was mutable over here when we Shadow it then it will be immutable again so what we can do over here is just using the mute keyword again so X stays mutable now you might ask okay why even shadowing we can just assign a new value to X why is this even a thing and you can see that here very good so y over here holds an integer type right integer 4. now when shadowing we can assign to the variable a a value of another type in this case for example a string right so y will then hold a string type right let's run that and as you can see this compiles now this line over here is completely useless I hope you notice that because we are taking X and assign it again to X which basically is the exact same thing as we had over here so completely useless of course they are doing that to learn the concept but in real life you don't need this line all right you can just do it like that and as you can see this also compiles unused variables fix the warning below with only one solution two distinct Solutions so as you can see of course we want two stores so we will do with two solutions now the problem here is that this program won't actually output warnings right so I will again copy that and I will put it and I will put it inside the rust playground like that so if we run this program now as you can see we get a warning unused variable X now I have showed you that before the easiest method would be to prevent the variable name with an underscore as you can see we don't get the warning over here the second way would be to do it like that this is telling the compiler that it should allow unused variables so if you run it again as you can see no warning so we have solved that destructuring we can use pattern with lead to destructure a tuple to separate variables now tuples we will cover that in a later topic but for now just think of tuples as a data structure that can hold um various values even of different types so fix the arrow below with least amount of modification so as you can see over here we have a tuple right holding two integers one and two and this over here is called destructuring so we are using the let keyword and we use this syntax over here to this structure these values into variables X and Y so X will then hold a value of 1 and y will hold a value of 2. now over here we are mutating X now again this variable here would be immutable because in Rust every variable in its nature is immutable and we have to explicitly state that we want to have it mutable so what we can do over here like we did before we use the mute keyword like that and then this should actually compile so X holds one here and then we increment X by two so extrude in the nthole 3 and Y should stay the same as it was and as you can see this is compiling the structuring assignments introduced in Rust 1.59 you can now use Tuple slice and struct patterns as the left hand side of an assignment so this is very useful actually so we can declare two variables at once so here we declare X and Y and this would be the same like writing let X and let y right so you're writing one line of code instead of two lines here now again we declare here the variables and we can then destructure these data structures over here so we have here a tuple and we are destructuring it and notice over here we don't need to use the let keyword again because we have already declared these variables over here right so we assign 3 to X and this value over here we don't care about so we can use these double dots over here now this over here is an array and again we will cover that don't worry and this array holds two values now we don't care about the first one but we want to destructure the second one into a variable called y now fill the blank to make the code work in this case we have here an array right remember assert EQ takes two arguments and both of them have to be the same equate to the same output right so what would be the value of x it would be 3. because we have this structured here 3 into the variable X and the same goes for here we have this structured 2 into a variable Y which means y will then hold two all right and we are done with the first topic easy start and see you next time now let's look at numbers in Rust and we'll start with integer types now an integer is basically a whole number meaning it doesn't have a fractional part and integers can come in two different forms they can either be signed or unsigned now a signed integer can represent both positive and negative numbers and I remember that because signed might stand for the minus sign that could be prepended to it and unsigned integers are always positive integers meaning they don't have a minus sign hence unsigned now integers can come in different lengths so there can be 8-bit integers 16-bit 32 up until 128 bits and each length over here has two distinct types one for signed and one for unsigned so for example if we have a variable which holds an unsigned 8-bit integer we would type annotate that with the type u8 or for example we have a signed 64-bit integral we would annotate it as i64. now the last element in the table over here is Arc and Arc stands for architecture now that means that the size of these two types eye size and U size are dependent on the computer's architecture now I guess most of you guys today run computers with 64-bit architecture meaning Isis new size have a size of 64 bits or 8 bytes All Right Now the default types in Rust are for integers i32 and for floats f64 so if we don't annotate anything the compiler will infer it to these types now my head is in the way now to make sense of all of this you will have to understand the binary number system now if you're familiar with binary just skip ahead because you don't learn anything new here but I just want to ensure that we are all on the same page so if we consider this decimal number over here 42 and let's look at each distinct digit so we would have 4 and we would have two right now 2 would be in the ones place and four would be in the tenths place and if we would have more digits here here would be the hundreds place thousands place and so on now as you can see we are here using a base of 10. why is that because in the decimal system we have 10 distinct digits to represent numbers right 0 to 9. and as you can see we are here doing an exponentiation with an exponent of zero because this is actually a mathematical rule which states that anything with an exponent of 0 will always evaluate to one so it doesn't matter which base you use if you have an exponent of 0 the result will always be one right 10 to the power of 0 would be 1 4 to the power of 0 would be one and so on and over here we have 10 to the power of 1 which would be 10 right and then what we do is just we are multiplying the digits of our number with these exponentiations right so we would for example here 4 times 10 to the power of 1 plus 2 times 10 to the power of 0. now again 10 to the power of 1 would be 10 and 10 to the power of 0 would be one and then we would multiply these numbers together and then add this and we would have 42 right something which is very natural to you and you don't even think about it right because decimal numbers are everywhere now in computers things actually look a little bit different let's for example again consider the decimal number 42 now this would be the binary representation of this number zero zero one zero one zero one zero and as you can see we have here eight digits now a digit is called bit right and a bit is the smallest possible unit of information a computer can hold okay now we have eight bits over here and eight bits make up one byte and we over here have actually the same logic as we did over here but with a base of two because again we can only represent two digits in a computer and that's because a transistor can only be in one of two states either it's conducting electric current or it's non-conducting zero or one right so the logic stays the same here we have 2 to the power of 0 which would be one because of the rule we have talked about 2 to the power of 1 would be two two to the power of 2 would be four two to the power of 3 would be eight and so on and as you notice the number here doubles one two four eight sixteen and so on now this over here is actually the fascinating part about computers to me personally because it's just crazy if you think that everything represented this video over here and graphical models and 3D games and everything is stored and represented with only two digits mind-blowing right so we actually here do the same thing we are just multiplying the bits over here with the exponentiations now we won't consider the zero bits because anything multiplied by zero will always be zero right so we are just caring about the one bits over here so we are multiplying one times two to the power of one which would be 2 right because 1 times 2 would be two then one times two to the power of 3 which would equate to 8 and 1 times 2 to the power of 5 which would be 32 as you can see over here and then we are basically just adding these numbers together and we have 42 and this is the way your computer stores all of your data all right now you might ask okay how far can we go with this what would be the largest possible number we can represent and let's for that see the range of 8-bit integers so the smallest possible unsigned 8-bit integer would be zero right I hope this makes sense because if we have a zero in every position over here the result will just be zero right now the largest possible unsigned 8-bit integer would be 255. and that's the case when every single position over here holds A1 right and again we are then just multiplying D1 by the exponentiations over here and add it together and you might now think okay 255 isn't a lot what if I want to represent bigger numbers and there is a simple solution to that you're just adding more bits over here right because the more bits we have the larger number we can represent so let's see for example a 16 bit integer again the smallest possible 16-bit integer unsigned would be zero and this is if every position over here holds zero now the largest possible 16-bit integer unsigned would be 65 535 okay and again this would just be if we had in every single position a one right again just adding the results of the exponentiations over here all together and we would have this result now as you can see the more bits we have the more we can represent now what about signed integers again sine integers can represent negative numbers so signed integers use a concept called tools complement where the processor will take a number over here for example 42 and this would be the binary representation of this number then it will invert this binary number meaning a 0 becomes a 1 and 1 becomes zero right just inverting and then after inverting the number we add one single bit okay so we add one here this would then equate to zero and we have a carry of one and then we add here one and we would have one and the rest says the same now this would then be the binary representation of minus 42. or negative 42. now the most significant bit over here is actually the sine bit if it is zero then it means the number is positive if it is 1 then the number is negative all right and that's why there are distinct types for signed and unsigned and the ranges for signed and unsigned integers vary right and over here we have an illustration just to see the ranges for example for I8 a signed integral of 8 Bits the range would be from -128 to 127. so the smallest possible number we can represent with a signed 8-bit integer would be -128 and the largest one would be 127. and as you can see again the more bits we have the larger and smaller the integer can go right and again unsigned integers never go below zero right then we have view size and eye size so these are as I've said architecture dependent so on a 32-bit architecture computer the size of these view size and eye size type would be 32 bits on a 64-bit architecture it would be 64 bits and this is also called pointer sized integer type because it matches the size of a word in a given platform so what is a word you have to understand that the processor does not read one byte at a time from memory it reads one word at a time so in a 32-bit processor it can access four bytes or 32 bits at a time and in a 64-bit processor it can access 8 bytes or 64 bits at a time now if we look at this very simplified representation of computer memory where we have over here addresses and over here the data and each location over here holds data of one byte all right now in a 32-bit architecture the size of a word is four bytes right 32 bits which means that the processor can access 4 bytes at a time so the processor will read this section over here as one word right it's basically unit of data and what you have to take from this is this statement over here you size gives you the guarantee to be always big enough to hold any pointer or any offset in a data structure so the U size type can hold memory addresses which can point to any location in computer memory that's very important so let's see floating point there are two types F32 which is of size 32 bits and f64 which is of type 64 bits now the representation of these floats are according to the IEEE 754 specification so let's look at some exercises tips if we don't explicitly assign a type to a variable then the compiler will infer one for us now remove something to make it work as you can see over here we have a variable X which holds an i32 integer right now over here we have immutable variable Y which holds an unsigned 32-bit integer right so over here we are trying to assign X to Y now this would actually fail because we can't assign a variable of a type to a variable of another type it's not possible so what we can do over here to fix that is actually just omitting the type and this will then implicitly hold an i32 type right because this is the default integer type in Rust now over here we are initializing in another variable set and what would be the type of Z right it would be I 32. all right thank you fill the blank so over here as you can see we have a value 38 which is of type u8 so we can also annotate a type directly on a value meaning this value over here 38 is of type u8 now the problem here is that the variable over here V expects a type of u16 so we can't initialize a variable which expects a certain type with another type but what we can do over here is using the as keyword which basically can convert an integer type to another integer type so we can use here s and u16 this will then compile tips if we don't explicitly assign a type to a variable then the compiler will infer one for us now we know that already modify a sort EQ to make it work so over here we are initializing the variable x with a value of 5. what would be the type annotation it would be i32 right it's the default integer typing rust now in this assert EQ macro we have here a string u32 we don't worry yet about this method I will explain that in a later episode and on the other side of the assert EQ macro we have a function call so we call type of with an argument of x actually a reference to X but again don't worry we'll cover that so when we call this function over here it basically just gives us back the name of the type so if we pass X over here we would get back i32 right because this variable over here holds a value of type i32 now this boot then actually fail right because we want here that type of X returns u32 so what we can do is just changing this type annotation over here all right fill the blanks to make it work now over here we have a data type for example I8 and the constant Max now Max just Returns the largest possible number that can be represented with the mentioned data type so for example I8 Max would be 127. right this would be an assigned 8-bit integer and we have seen the ranges now the max for an unsigned 8-bit integral would be 255. all right fix arrows and panics to make it work now over here we have a value 251 which is of type u8 and we add 8 to it now in this case the compiler will infer the types so this would be 8 and the variable itself will hold them u8 now can you spot the problem over here the problem is we have seen that u8 can only represent up to 255 right so over here we would have 259 so this is not possible so what we can do over here is just changing that to the next bigger data type for example u16 now then we have here a data type I8 assigned 8-bit integer and we call the checked add method now this is actually the same as doing it like that but in a safer way because we have to handle the error and unwrap over here we will cover that so this is actually a little bit safer but it still will panic because again the I8 cannot represent 259 it would be an overflow so again we can over here just say we want an i16 because an i16 can without problems represent 259. so V1 will then hold u16 and V2 will hold i16 . again I'm just doing this for clarity you don't have to annotate the types alright modify assert to make it work now as you can see over here we have numbers in different representations or basically in different number systems so this is decimal this is hexadecimal this is octal and this is binary okay now the decimal over here has an underscore and this is just a delimiter so this is just for readability it doesn't affect the actual value now let me convert that over here we would have 2024 this is already in decimal system then over here we would have 255 then over here we would have 63 and then over here I hope you remember this would be 255. now if we calculate this as you can see the result would be this value over here like that and this basically proves that in Rusty you can perform mathematical operations on different number systems right now floating Point fill the blank to make it work as you can see over here we have a floating point value so what would be the type I will annotate it directly so it would be f64 this is the default floating Point type right now over here we have an F 32 and over here we have an F 64. right like that again type annotations are not necessary now if we pass x to the type of function over here what would be the return value it would be f64 right make it work in two distinct ways so if we execute this program over here as you can see this would panic now the problem here is a floating Point precision the result won't be exactly 0.3 it would be something like that maybe right or something like that I don't know exactly but t floating point value is too precise because the default floating Point type is f64 so this is really precise and that's why you have these tiny fractions over here now we can solve this using an F 32 Which is less precise right like that and this succeeds now two distinct ways we can also use the as keyword like that so let me first move my head over here again so range two goals first modify Sr to make it work second make print line output 97 to 122. now over here as you can see we have immutable variable sum what would be the type annotation it would be i32 okay now over here we have a for Loop meaning we are iterating over some range and then we are executing an instruction inside this code block over here multiple times basically the times we are iterating over this range over here now in each iteration the variable I will hold a different value right so in the first iteration it would be -3 in the second iteration it would be -2 in the third minus 1 and so on until we reach 1. y1 because 2 over here is excluded and that's always the case when using this syntax the end point of the range is always excluded so we are iterating from -3 to 1 okay now let's actually do these iterations manually so we start with zero right now in the first iteration I would be -3 so 0 plus minus 3 will be minus 3 right so sum will then hold -3 now in the second iteration I will be minus two so minus three plus minus 2 will be minus five third iteration minus one minus five plus minus one will be minus six then in the next iteration I will be zero so it doesn't have any impact and in the last iteration I will be one so minus six plus one will be minus five so the value of sum after this for Loop will be minus five now over here we again have a for Loop and this time we are iterating over a range of characters namely from A to Z now this time Z over here is included because we are using the equal sign over here which means we want a range from A to Z where the end point set is included and C over here will then hold in each iteration a character so in the first one it would be a then it would be B then C and so on and we just print out the characters let's see how that looks like as you can see just printing out each character from a to z now we want to have an output of 97 to 122. so let's look at the ASCII table as you can see in memory a character would be stored as a numerical value now of course in memory everything is stored as binary numbers but it would be stored as the binary representation of these decimal numbers right here so we want to go from 97 a to 122 Z right so it's actually pretty simple instead of outputting DC as character we want to Output it as an u8 for example right and then as you can see we have the ASCII code for the characters from A to Z lowercase right then over here exercise 10 fill the blanks so we are here importing from the standard Library the Ops module range and range inclusive now we have seen the shorthand syntax and this would actually be the more verbose way to write that but I think no one actually does it this way over here we have a range which starts at one and ends at five now again five would be excluded right like that now range inclusive means we range from 1 to 5 where 5 is included and we would write that short and syntax like this one two five and five is included right let's see the last exercise in this topic computations fill the blanks and fix the errors so as you can see over here we have integer additions so one plus two will be three and the compiler over here will infer these types so because we have over here an u32 an unsigned 32-bit integer it will infer these values over here to be of the same type all right now over here the same thing we have 1 minus 2 would be -1 and the compiler infers these types then over here we have 1 minus 2 which is equal minus one the problem over here is that we are dealing with an unsigned 8-bit integer now remember unsigned integers can never be negative so what we can do to fix that is just converting it to an I8 assigned 8-bit integer and signed integers can represent both positive and negative numbers now over here we would have 150 and if there are no type annotations here the default integer type will be inferred which will be i32 now over here again we have the problem of floating Point Precision so we can cast that to an F 32. like that over here we have the modulus operator and the modules operator basically gives back the remainder of a division so if we divide 24 by 5 we would have a remainder of 4. now as you can see here we have Boolean logic and bitwise operations and I want to show you some slides so we get the concept let's start with Boolean logic Boolean logic deals with values that are either true or false and there are three basic operations and or and not and over here you can see the truth tables of these operations so let's see the and operator now the end operator takes two inputs and produces one output now the only time it outputs true is when both of the inputs are true otherwise it will always return false in an or operation if either one of the inputs is true or both of the inputs are true then it will output true if both of the inputs are false then it will output faults and the not operator will just basically invert the Boolean value so over here we have not false and this would evaluate to true and not true will evaluate to false now let's see bitwise operations bitwise operations are operations that manipulate individual bits that make up a binary number treating each bit of a binary number as a separate unit and perform logical operations on them so it's very important that you get that in bitwise operations we are treating each bit as a separate unit and we are doing some manipulation on each individual bit and not the binary number as a whole and we'll cover here and or X or operations and bitwise shifting now there are a lot more but I will just cover the ones in the exercise let's start with the end operator this Ampersand symbol over here and returns one only when both of its inputs are one so similar to Boolean logic as you can see the end operator takes two inputs and produces one output now as you can see in the truth table if both of the inputs are one then the output will be 1. in any other case the output will be zero let's see the or operator this symbol over here all returns one if at least one of its inputs is one if both inputs are zero the output will also be zero so same thing as in Boolean logic if either one of the inputs or both of the inputs are one then it will output one so the only case it outputs 0 is when both of the inputs are zero and let's see xor xor or exclusive or returns one if the inputs are different and zero if the inputs are the same so it's basically similar to the or operator with the difference that if both of the inputs are one then it will output 0. so the inputs must be different so it will output one all right if they are the same it will output 0. and this is basically a building block of your computer so your computer is made up of logic gates and these logic gates perform these operations all the time millions of times in a second then let's see bitwise left shift so we have over here the decimal number one this would be the binary representation now we want to left shift by five positions so we take this bit over here and move it five positions to the left so the so this bit over here will be at this position after the DP twice left shift operation all right and that means we then have a value of 32 because this is the binary representation of decimal 32. and bitwise right shift is basically the same now over here we have a hexadecimal value and we want to right shift it to positions now this value over here would be 128 in decimal and this would be the binary representation of 128. now we want to right shift it to positions meaning we take this bit over here and shift it two positions to the right meaning the bit will then be in this place and again we would have 32 because this over here is the binary representation of that number all right let's see so true and false would be false right because in an and operation both of the inputs must be true in order for it to Output true now true or false would be true because in an or operation only one of the inputs have to be true and not true would be equal to that's right false fight like that now let me run the program so we can see the result of these bitwise operations so as you can see over here zero zero one one and zero one zero one is zero zero zero one why is that because again we are here considering each individual bit as a unit and Performing manipulation on each a distinct bit now over here as you can see we are taking the rightmost bit over here and we are performing The End operation with the rightmost bit over here meaning one and one would be one right because and if both of the inputs are 1 then it will output one in any other case as you can see one and zero would be zero 0 and 1 would be zero zero and zero of course would be zero now let's see this or operation we have one or one this will output one we have one or zero which will output one again right because in or operation if either one of its input or both of them are one then it will output one zero or one is one and zero or zero of course is zero and then we have xor one X or one put B zero because remember X or the inputs must be different in order for it to Output one like over here for example 1 X or zero would be one right the inputs over here are different one and zero same over here 0 x or one would be one and zero X or zero of course will be zero and as you can see over here these are the bitwise shifting so I've covered that in the slides all right see you in the next topic so we will cover in our Char pool and unit let's start with characters so over here make it work now size of Val will just return this size in bytes of a specific value so over here we are initializing the variable C1 with a character so C1 holds a type of char then we pass to the size of valve function the variable C1 and this will return the size of this character in memory in bytes right and same thing over here we have C2 also holding HR and we call the size of L function with this variable so let me first print out the actual result of this function call so if I come over here and I will do a print line statement and just basically outputting the result of this function column so let's see as you can see the output would be 4 meaning this character over here will take up four bytes in memory so the size of C1 is 4 bytes now in Rust the chart type is big enough to hold every single Unicode symbol meaning it is of size 4 bytes so it's able to hold any Unicode scalar value all right and the same thing over here the output would be four meaning if we call this function we will get back four all right make it work again initializing a variable with a Char then we are calling the printshar function with an argument of this variable as you can see this function over here this is the function signature this function takes as argument C which is of type Char and then we will and then we just print out C now the problem over here is that this isn't actually a Char this would be considered a string because in Rust there is a difference between double quotes and single quotes So double quotes are four strings and these single quotes are for characters foreign out the character let's see booleans make print Line work as you can see this variable underscore F holds a Boolean value of false and over here T also holds a Boolean value of true now in an if block this conditional over here will be checked if the result evaluates to true then this instruction inside the curly braces will be printed out if it evaluates to false then the program will just ignore this instruction over here now in this case we want to print that out but over here the result would evaluate to false because we are saying not true right because T holds a Boolean value of true so not true would be false that means this will never get printed out what we can do over here is just removing this not operator and then it will work make it work again we have some variables that hold Boolean types now again this variable over here T will also hold a Boolean type because this over here is an and operation and remember an end operation takes two inputs and produces one output so the output type will be of type pool now true and false would evaluate to false and then we are comparing T and F now F holds true and T holds false which is not what we want so what we can do over here is just making this false over here and then both of them hold a Boolean value of false unit type make it work don't modify implicitly red unit so the unit type is a type which doesn't hold any value its size is zero bytes and it's usually if a function doesn't return any value then a unit type will be returned and this happens implicitly so you don't have to annotate that let's see an example over here the underscore V variable holds a unit type as you can see a unit type is basically represented as an empty Tuple and this would be the type annotation right so over here we are holding a tuple with two i-32 values then as you can see we have here an assert EQ macro we are comparing V with the output of this function call let's see this function over here implicitly red unit so all this function does is it will print out something and then goes back to the color now this function over here doesn't return anything and if a function doesn't return anything the compiler will implicitly return a unit type right but you don't have to annotate that all right so the return value of this would be unit type now V over here holds a tuple meaning this would fail we have here to provide underscore V which holds a unit type so this holds a unit type and this function call over here will return a unit type all right what's the size of the unit type modify 4 in assert to make it work as you can see we are again having this size of L function and we are providing it this variable which holds a unit type now the output would be zero because unit types are of size 0. so to quickly recap HR is a single character of size 4 bytes a Boolean value of true or false is of size 1 byte and the unit is an empty Tuple of size 0 bytes used to return nothing in expressions or functions so this time I will first show you the slide and then we will solve exercises so a statement is an instruction that performs some action but does not produce a value function definitions for example are statements as well as code that ends with a semicolon usually and an expression will evaluate to a resultant value so let's see all right let's see this example over here we have X which holds an u32 all right now over here we are initializing the variable y with the resultant value of this whole expression so as you can see we can inside these curly brackets we can declare variables over here and then we do some operations and return the value as you notice I omit over here the semicolon that means that this result of this operation will get returned which means when this evaluates it will then be assigned to Y so y will then hold the result of this operation now all of the code inside these curly braces are considered as an expression because it produces a resultant value right it would be this value over here now this variable assignment over here would be considered a statement because first of all it ends in a semicolon and second it doesn't produce a new value right it just assigns a value to a variable now y in the end would hold an u32 right because we are here performing an operation with values of u32 types the semicolon suppresses this expression and unit type is assigned to Z so Z over here would hold a unit type why is that because we over here have a semicolon so this code over here doesn't return a value if we omit the semicolon then this would be the return value that then gets assigned to this variable meaning it will then hold a u32 all right let's do exercise one make it work with two ways so as you can see over here we are initializing this variable over here with this expression inside the curly braces now we are here initializing the mutable variable x with a value of one so X will hold I 32. then we are mutating X incrementing it by two so X should then hold 3 right now over here this would actually hold a unit type because over here we have an assignment so we are adding 2 to the value of x and then we assign it to X now the problem is that variable assignments are actually statements so this should end in a semicolon because this would be the same as if I would write it like that right so we can't return over here a variable assignment but what we can do is we just return X because X over here will then hold 3 and then we return X meaning the value of x is then assigned to the variable V so variable V will then hold a type of I 32. let's see this compiling as you can see make it work in two ways now the second way to solve it is just in this assert EQ macro we compare V to a unit type because um no value will get assigned to V and that means the variable we will then hold a unit type like that over here as you can see we are initializing V with a variable assignment so this would not be allowed it's invalid syntax but what we can do is we use curly braces like that and then we initialize here a variable X and we return X this would be allowed so V then holds an i32 let's see over here we have a function call with two values one and two so let's see the function over here it takes two arguments X and Y of type i32 and it returns an i32 value now over here as you can see this function just adds X Plus y now the actual return type over here would be unit type because as you can see we are ending this operation with a semicolon meaning nothing will get returned so if we omit the semicolon over here then we would have a return type of I 32. meaning after the function call over here s will then hold the result of this operation 1 plus 2 would be 3 so s would hold three that would be a type of i32 so let's now look at functions a function is a block of reusable code that performs specific tasks it can take arguments processes those inputs and then returns a result and a diverging function is a function that never returns to the caller and this would happen for example if the diversion function is panicking looping forever or quitting the program so let's see don't modify the following two lines as you can see we have here a tuple and we are destructuring it we have already seen how that works now over here we have a function called now this would be the color and this function would be the collie okay when we call the function the flow control will go to this function and the program will continue executing the instructions provided in this function now it's very important that you know that functions always have to annotate types for their arguments so over here we would Define that X should be of type i32 right because Y is I 32 and we are performing here a mathematical operation meaning they both have to be of the same type and as you can see like in the last exercise this function wouldn't return a value because over here we have a semicolon so if we omit that then this result of this operation will get returned which means the return type would be i32 then when this Returns the flow control will go back over here and assign The Returned value from this function to variable s meaning the variable s will then hold a type of i32 and the value would be 3 right because we are calling the function with arguments one and two or X and Y over here which hold one and two and then this function will just sum it up together and that means s will hold 3. over here we are calling the print function so we go to the function over here and as you can see the function just prints out something to standard output but it doesn't return a value now implicitly the compiler will return a unit type like that but we don't have to write it it's implicit solve it in two ways don't let print Ln work all right so we are here calling a function never return and this function should never return because as you can see the return type over here is defined as exclamation mark and that means this is a diverging function a function that should never return back to the caller now we can do this in various different ways but I will just use the most simple one which is panic and the Panic macro just causes the program to panic meaning the program will immediately exit and return an error as you can see unreachable statement so this line over here is never reached as you can see and that's what we want because over here don't let print Ln work so we never actually reach this point the program will exit before going back to the caller diverging functions diverging functions never return to the caller so they may be used in places where a value of any type is expected so in the main function we just print out something and over here we have a function get option it takes as argument a value of type u8 and returns option i32 now we will cover options in a later episode now over here we are matching TP and matching if you're coming from another programming language is like is which block okay we are matching TP and then if for example TP would be one then this code block over here would get executed if it is anything else then this code block will be executed and we will cover match statements but match is just a much more powerful way of pattern matching than for example if else conditionals so after this match we are calling this never return FN and this should never return so there are some macros we can use here I've showed you one the Panic macro like that or we can also use unimplemented and you use the unimplemented macro if you have a function that is not implemented yet and we can also use it to do macro like that and to do is basically very similar to and implemented and all of them will cause this function to not return to the caller in this case this would be the color over here fill in the blank so as you can see we have here a variable B which then gets matched over here so if B holds a value of true then 1 will be assigned to variable V if B holds a value of false then it will print out success and the program will then panic now we can here assign false because I guess we wanted to print out success right and yeah let's see as you can see this would be the compiler message if a program panics trade main panicked at we have no value for false but we can panic that would be this output over here so we will now cover ownership and ownership is a concept which is unique to rust meaning even experienced programmers can find it difficult to understand this concept but it's very logical and it all makes sense and I will use visuals and illustrations to make it easier to understand the concept so ownership is basically a set of rules that govern memory management these rules are enforced at compiled time if any of the rules are violated then the program won't compile now let's see the three rules of ownership in Rust first each value in Rust has an owner second there can only be one owner at a time third when the owner goes out of scope the value will be dropped now the owner of a value is the variable or data structure that holds it and is responsible for allocating and freeing the memory used to store that data foreign let's look at scope a scope is arranged within a program for which an item is valid we have two types of scope one is global scope and that means a variable that is declared in global scope is accessible throughout the entire program and local scope if a variable is declared locally it means it is accessible only within that particular function or code block which in turn means it's not accessible outside of that function or code block let's see an example of scope as you can see we are here initializing the variable s with a string that means s over here is the owner of this data of this string value okay now s is valid from this point forward so after initializing s it will be valid throughout this scope over here which is defined by these curly braces so inside of this scope we can do stuff with s when this scope is over s is no longer valid so as over here the owner of this data will deallocate the data that is stored in computer memory and that means in turn we can't use as after this point it gets dropped or removed from memory so when s comes into scope it is valid it remains valid until it goes out of scope and a general rule is the scope ends where the block of code ends so to understand ownership you have to understand computer memory now memory is a component in a computer to store data and instructions for the processor to execute there is a type of memory called random access memory or RAM and it is volatile meaning when you're turning off your computer all data stored inside Ram is lost and there are two types of regions in Ram which is used by your rust program it is stack memory and Heap memory so now let's look at stack memory and I want you to imagine a stack of plates now if you want to eat something you take from the top of this stack a plate put food on it eat and then you will clean up the plate right when you're done cleaning you put the plate back on the top of the stack and that's exactly what is happening in stack memory so this concept is called last in first out meaning the last thing that is pushed on top of the stack will be the first thing that will get popped from this deck meaning the first thing that will get executed now all data stored on this deck must have a known fixed size like integers floats Charles bulls and so on basically all the types we have seen so far are all of fixed known size at compile time now pushing to the stack is faster than allocating on the Heap because the location for new data is always at the top of the stack meaning the program can access a data and stack memory at constant time if you are familiar with time complexity and types of unknown size will get allocated to the Heap and a pointer to the value is pushed to this stack because a pointer is fixed size don't worry I will show you illustrations which will make you understand this concept better let's see for example this simple rust program so as you can see we are starting in main because main is the starting point of execution in every rust program so over here we declare some variables X Y and Z meaning these variables are local to the main scope to this code block over here and because they are local they can't be accessed outside of this scope now over here we have a function call and we pass it the value of x and of Y right so as you can see the add numbers function takes two arguments of type i32 then over here we declare a new variable and assign and we assign the result of the operation a plus b meaning the past value is X Plus Y and we assign it to the variable C and then we just return C right so then Z over here will hold the returned value now when we start execution main alongside with all the local variables will get pushed to the stack then because we have over here a function call inside Main the add numbers function alongside with all the local variables to this function will get pushed to this stack if we would have other function calls over here or inside here then this will also get pushed on the stack and so on and then the program will just execute everything that is on the stack from top to bottom now let's look at Heap memory date of no known fixed size belongs on the Heap allocating data on the Heap will return a pointer an address to a location where the data has been allocated allocating on the Heap is slower than pushing to this stack and accessing data on the Heap is also slower as it has to be accessed using a pointer which points to an address so if we look at this abstract illustration of Heap memory as you can see we are allocating to Heap memory if the size of of the type we are allocating is not known at compile time and the types can then also be dynamically grown or shrinked right the size is not fixed and we will see examples of these types where this cat where this could be useful so when we allocate to Heap memory let's see for example the this packet over here we will get back a memory address and we need this address so we know where this specific data over here has been allocated so we then can access it in our program and an example of a heap allocator type would be the string type now as I've said all types we have covered so far were fixed size meaning their size was known at compiled time and they can't grow and Shrink so the difference is a string is mutable meaning its size can change at run time and a string is stored on the stack with a pointer to the Heap so when we initialize a string type then we will get back a pointer which then points to the actual data in the Heap and and the actual data of the string is stored on the Heap so let's see an example to understand this better as you can see we have here the variable S1 which we initialize with a string type over here and we initialize a string like this string double colon from and then these the actual string now you need to understand that the variable S1 doesn't hold the actual string in this case it holds a pointer which points to the data that was allocated on the Heap so if we look at S1 we can see that S1 doesn't hold the actual data but it holds a pointer and a pointer is basically a variable that holds a memory address that's what we call pointer and we have here Len and this is the length of the string we have initialized and the capacity holds the information of how much space was reserved in Heap memory for this specific type in this case we have initialized a string with a length of 5 so the capacity will be 5 so 5 locations have been allocated in Heap memory so this place is reserved for this string type and we will go into a much greater detail about capacity but for now when we initialize a string with a length of 5 the capacity will be five it will be the same so the size of this S1 variable will be 24 bytes because each field over here is of Type U size right so the pointer is of Type U size the Lan anti-capacity all of them are U size and u-size assuming we are on a 64-bit computer will be 8 bytes so 3 times 8 bytes will be 24 bytes meaning the size of the variable S1 is known at compile time because again the variable S1 will get pushed to this stack memory and everything that lives on the stack memory has to be of known fixed size at compile time in this case all of these fields over here are known fixed size but we are pointing over here to the actual data in Heap memory which can be dynamically sized right it can grow and shrink and we will see examples of that so that is actually the point important the S1 variable doesn't hold the actual data but it holds a pointer to the data in Heap memory okay basically a pointer to the location of the first element and by the way in Heap memory this data over here is allocated as a contiguous block meaning in Heap memory these are all in a contiguous sequence of memory addresses right that's why we only need the memory address of the first location and a length and then the compiler will know where to look at and how long the sequence of data is so copy versus move scalar values with fixed size all types we've covered at the beginning will automatically get copied in this stack copying here is cheap dynamically sized data won't get copied but moved copying would be too expensive let's see an example over here as you can see we have initialized a variable x with a value of 5. then we assign X to y now the type of this value would be i32 right because it's the default integer type now because i32 is fixed size it means that it lives in this stack memory and everything that lives in stack memory will automatically get copied meaning we are here assigning a copy of x to y or basically a copy of the value 5. so we are not assigning the actual value but a copy of that meaning Y and X would point to different memory locations the value is the same but the location at which the value is stored is different because again when we assign a fixed size integer that lives in stack memory rust will automatically copy the value because copying on the stack memory is cheap now over here as you can see we initialize a string and then we assign S1 holding this string 2s2 now as we've seen S1 doesn't actually hold the data over here but it holds a pointer which is returned when we are allocating this data over here this string on the Heap memory so the allocator that allocates this data on the Heap memory will return a pointer and that means if we assign S1 over here to S2 then S2 will only get a copy of the pointer and not the actual data so that is how that might look like as you can see we have S1 which is initialized to hold a string now S1 will then be assigned to S2 and as you can see this is how this might then look like we have here the variable S1 and S2 now the actual data over here didn't get copied the one thing that got copied was the pointer alongside with the metadata meaning these two variables now point to the same location in memory right now this would violate the second rule which states that there can only be one owner at a time so we can't over here have two owners to the same data in Heap memory so what rust will do is it will drop S1 it will delete S1 and that means S2 will then be the owner of the data so the first variable S1 will be dropped and cannot be used after assigning it to S2 to avoid dangling pointers so after assigning S1 to S2 we can't use S1 again and that's different like we saw over here with for example integers which are fixed size we can use after assigning X to Y we can use both of these variables X and Y without any problem because again here the value got copied so the compiler will drop the variable S1 meaning S2 will become the owner of this string and that means we can't use S1 after this point now what if you want to actually have a deep copy meaning you actually want to copy the data that is allocated in Heap memory we can do that but we have to explicitly state that we want to do that because as I've said this might be expensive if you have large data that is allocated on the Heap and if rust would implicitly copy all the data all the time you're assigning variables then this would be really expensive and that's why we have to explicitly call over here the Clone method so when we take S1 which holds a string and we call upon it the Clone method and assign this to S2 then that is what this might look like as you can see S1 again doesn't hold the actual data but a pointer to the data that is that has been allocated in Heap memory and when we now look at S2 S2 also holds a pointer to data that has been allocated in Heap memory the difference is that the actual data in Heap memory that S1 is pointing to has been copied in Heap memory so S1 and S2 point to different locations even if the data is the same right and that's different from what we saw here as you can see S1 and S2 point to the same location in Heap memory I hope you get the concept so let's now see how ownership Works in functions as you can see we have here a variable s which we initialized with a string type so over here s comes into scope and in this case s will be the owner of this data then over here we are calling the takes ownership function providing it an argument of s so s value moves into the function and so is no longer valid here meaning the function takes ownership is now the owner of s so as you can see this function over here takes an argument of string type and it just prints out the provided string so as you can see some string comes into scope and some string over here is the string we have provided as an argument then we print it out and over here some string goes out of scope and drop is called the backing memory is freed meaning when we reach the end of the scope some string will get deallocated in Heap memory so after this point over here s can't be used in this main function over here because again the ownership has been moved from variable s into the takes ownership function now over here we have another initialized variable x with a value of five now five would be of type I 32 the default integer type meaning 5 is a type that lives on this stack it's pushed to the stack and not allocated on the Heap and that means when we call this function makes copy and provided X over here then we provide this function a copy of the value and not the actual value itself so we provide here a copy of the value 5 right and then over here we just print out this number then here are some integrals out of scope nothing special happens so the argument we are providing here goes out of scope but X over here is still valid because again we have just provided a copy because this is a type that is living on the stack memory so let's see this example over here we are calling the gifs ownership function now when we look at this function over here we are inside the function code block initializing a variable with a string value over here and then we are just returning this string and that means that S1 will then hold the returned string namely this yours string over here so S1 will be the owner of The Returned string type over here we are initializing S2 with a string over here that means S2 is the owner of the string then we are calling here the function takes and gives back with an argument of S2 meaning takes and gives back will become the owner of this data and that also means that after calling this function we can't use the variable as 2 because it has been dropped now when we look at the function takes and gives back it takes an argument of type string which we are providing here and then it just again Returns the string right so S3 will then hold the return string basically S3 then became the owner of this data right so we have transferred ownership from S2 to this function takes and gives back and then from this function to S3 and then at the end of the main scope S3 goes out of scope and is dropped S2 was moved we've seen that we have moved it inside the function so nothing happens S1 goes out of scope and is dropped so the variable S1 also gets dropped because it is the owner of of this string type returned from this function so you are now asking why do I even care about all this stuff in other languages I don't have to worry about all of this and this seems rather complicating a simple issue right now the main point of all of this is that ownership prevents memory safety issues so by this concept of ownership you are preventing dangling pointers pointers that point to Nowhere or some garbage values double three which is trying to free memory that has already been freed or memory leaks not free memory that should have been freed and if you're coming from C or C plus plus you're you know exactly what I'm talking about here so before stepping into borrowing we will do some exercises concerning ownership so let's see number one use as many approaches as you can to make it work so as you can see the variable X over here has been initialized with a string then we are assigning X to Y so y also should hold a string the problem over here is that we try to access X over here after it has been assigned to y now we have seen in the example that X will get dropped after we assigning after we are assigning it to another variable right so we can't access X but what we can do is telling the compiler that we want a deep copy of the data that X is holding and we are using for that the Clone method and then we X and then we can access both of these variables because the data has been cloned and X still lives on number two don't modify code in main again S1 has been initialized with a string and then we are calling here take ownership and providing it an argument as two as you can see the function take ownership expects an argument of type string and it just prints out the provided argument now we are then assigning the return value of this function to this variable the problem here is that this function actually doesn't return anything so this would hold a unit type right now if we want to return the argument over here after Printing and using it we can just return it like that and if you return something from a function you have to annotate the return type and that would be string right because we are taking here in the string and then we are returning it back that means S2 will then hold the returned string and then we can print out S2 and notice S1 can't be used after we have called this function with an argument of S1 because the ownership has been transferred to this function so meaning S1 is holding the data over here which means it's the owner then we call this function and provide it as one meaning this function over here becomes the owner and then we are assigning the return value to S2 so that means S2 over here will be the owner of the data in the end number three we are here calling the function GIF ownership and as you can see inside the function we are initializing a variable s which holds a string and we call here the into bytes method on S now let me show you that in the documentation so this is the in two bytes method now as you can see converts a string into a byte Vector so if we have a string over here and we call the into bytes method then we will get the string over here but represented as a vector of bytes okay and the important thing here is that this consumes the string meaning s over here will get consumed and that means s can't be used after this point now what we can do as an alternative we can use S bytes and S bytes just takes references and references will be our next topic so don't worry too much about it right now but when we call the S bytes method over here then the owner will remain the same right it doesn't consume the data so what we can do over here is calling the S bytes method and that means s Remains the owner of this data and we can return it then and that means s over here will be then the owner of The Returned value and then we just print out s fix the arrow without removing code line again we are initializing s with a string then we are calling the print store function with an argument s and all we do is we then print out the string but over here again we try to access S and that would be a compiler error because we are over here providing S as an argument to this function over here meaning this function will become the owner of the data s is holding and that means s is not accessible anymore what we can do over here is we can call the Clone method meaning we are providing this function over here a copy of this data so this data will get copied in Heap memory and that means s will still be valid don't use clone use copy instead so over here we have a tuple now this Tuple holds two integers a unit type and a string now let me annotate that we annotate tuples like that and we have to find each type of array value distribute so we have 2i32 integers One units type and one string now over here we are calling the Clone method and assign it to Y so y should actually hold the same uh values all right and because we have called the Clone method we can use both variables without the problem now it says don't use clones so we can't use clone over here now my solution to this would be if you take a look at the types this Tuple is holding then we see that most of the types over here are actually on the stack memory meaning they are of known fixed size right so inters and i32 would be of size 32 bits right a unit type would be of zero bits the only problem arises here because we have a string now a string is a heap allocated type meaning its size is not known at compile time now what we can do over here is we can use a string literal now we will cover the difference between strings and string literals but for now a string literal is a string that is hard coded into the binary itself into the program so it is immutable which means that this size is known at compile time so all T types the Tuple holes are known are of known fixed size at compile time so over here we can do the same and what's the reason we have been doing this because we know that a type that lives in stack memory meaning it's fixed size will get copied implicitly now this Tuple only holds fixed sized types meaning the Tuple itself is fixed size and that means X over here will get copied implicitly by the compiler all right mutability can be changed when ownership is transferred as you can see we are here initializing s with a string then modify this line only we are here assigning s to S1 now over here as you can see we are modifying the string that S1 is holding and remember when we assign s to S1 S1 will become the owner of this data meaning s will get dropped so we are then trying to mutate S1 and we are pushing to it D string world now remember that a variable is immutable in its nature so we have to use the mute keyword over here to denote that S1 should be mutable and that means that if the ownership has been transferred to another variable we can change its mutability right even if s was immutable we can make S1 mutable without problem because S1 is now the owner of the data now over here as you can see we are initializing a variable x with a boxed integer type now box allows you to allocate directly on the Heap as you know this is an i32 and i32 is normally live in stack memory when we use this over here Box new and we box this i32 it means it gets allocated on the Heap and that means X will then hold a pointer to the data that was allocated in Heap memory so the type annotation here would be box and this box holds an i32 and X then basically holds a pointer now over here implement this line don't change other lines now as you can see we have over here to declare y now y gets the referenced and that means we have over here to get back a pointer so we can do basically the same thing like we did before we can use Box new and let's do one for example so the variable one will hold a box with a type of i32 and as I've said we allocate this integer on Heap memory meaning we get back a pointer and the pointer is a memory address to the location where this data has been allocated now because y holds a memory address we can't assign a value and integer value directly to Y because Y is just holding an address and not an i32 type because remember we can just assign a value to a variable if both of them are of same type now as you can see y holds the type of box i32 which is not the same type that means we have to de-reference and we are dereferencing using this star operator and and this star operator means we go to the location of Y because Y is holding a memory address and we want to access the value right so we want to access this interval that has been allocated on the Heap and that means we can assign a new value 2y but we have to use this store operator and again over here you can see it even better this would be equal right because X is pointing to a location in memory which holds an integral value of 5. now if we would omit the star operator this would not match because X would hold something like that right in memory address now this memory address and the integer 5 would not be equal but this star operator basically says go to this address and give me back the value that is stored at that address which would be 5. and of course we have to make this variable over here mutable right because we are here mutating y partial move within the destructuring of a single variable both by move and by reference pattern binding can be used at the same time doing this will result doing this will result in a partial move of the variable which means that parts of the variable will be moved while other parts stay in such a case the parent variable cannot be used afterwards as a whole however the parts that are only referenced and not moved can still be used so over here we have a lot of stuff we didn't cover yet and I will try my best to explain it but if you don't get it 100 it's no problem okay we will go into greater detail on each of these structures and types so as you can see over here we have a struct person now a struct is basically a custom type we can create our own types in Rust and a struct basically is a template we are saying here as you can see this truck holds two members one name which is of type string and one H which is of type boxed u8 okay a heap allocated unsigned 8-bit integer then we have to instantiate and this person struct meaning we are using this template and create basically a concrete value of that so we are providing here for the name member a string like it's defined here and for the age we provide a boxed value of 20. now this person variable will then hold a type of person right this is now our own custom defined type now name is moved out of person but age is referenced so over here we are destructuring the person instance over here and we do that very similar to tuples we have seen we used to let keyword the name of destruct and then we the structure the fields so the person so the person instance over here will get the structure meaning person name will get put into the name variable and the H member here will get put inside this H variable now notice something we are here using the ref keyword meaning th variable will only take a reference of this data and not the actual data itself but name will get moved inside this variable so the name variable here will be the owner of this string right and that means that person is not the owner of this string anymore it has been when instantiating the struct but it is not anymore then as you can see we are printing out these variables but we then can't use the person instance right because the person instance is not the full owner of all team members inside it and also we can't access person.name this is how you would access normally a member in a struct person Dot and then they member in this case name but we can access person.h because the person instance over here is still the owner of age because again we have destructured it and the variable H only took a reference again if you don't understand a hundred percent it's fine okay we haven't covered a lot of this stuff going on here and I even think this is not a good example right now but yeah so exercise eight as you can see we have here a tuple holding two string types now we are assigning the first element of the Tuple and by the way Cupids are also zero index like arrays and we can access the indexes like this the name of the variable and then Dot and the index we want to access in this case the first one that means that underscore s will hold a type of string right namely this over here and that means the ownership of this string data here got transferred to this variable so underscore s is now the owner of this data meaning we can't access T again because T is not the owner anymore of all the values it contains in the Tuple right so we can't use it again but we could still use T at index 1 because this over here is still owned by T because only the first element has been moved out and over here again a tuple with two strings so fill the blanks as you can see what we want here is destructuring the Tuple and we want to initialize these two variables S1 and S2 and remember when we destructure it we have a syntax like that now over here as you can see when we are destructuring this Tuple both of the values it is holding will get transferred to these variables so S1 will be the owner of this stream and S2 will be the owner of this string meaning T doesn't own any values after this point but what we can do because we want to access T again over here we can call the Clone method as you've seen meaning S1 and S2 only hold copies of these strings and not the actual values and that means that t will still be accessible and T is still the owner of all the values inside the tuber alright see you in the next topic so let's look at borrowing borrowing is a way of temporarily access data without taking ownership of it when borrowing you're taking a reference a pointer to the data and not the data itself its goal is to prevent Dengue pointers and data races data can be borrowed immutably and mutably and there are certain rules when borrowing which we have to comply with otherwise the program won't compile let's look at these rules the first rule states that at any given time you can have either one mutable reference or any number of immutable references but you cannot have both you either have one mutable reference or any number of immutable references and the second is references must always be valid so let's see an example of a reference over here we are initializing S1 with a string then we call the calculate length function and as you can see we are here passing a reference to S1 and this is indicated by this Ampersand symbol over here so then let's see the calculate length function as you can see the argument should be of type reference to a string which we did provide right it's a reference this Ampersand to S1 which is of type string and then all this function does it will call the Len method which will return the length of the string and it returns it now notice that the return type will be of Type U size and then this Len variable over here will hold the length of this string and the type annotation would be use size right the return value of this function and then we just print out S1 and length and notice something over here we can use S1 because S1 over here stays the owner of the data we have just provided here a reference to S1 and not S1 itself so let's see how that looks as you can see we have S1 now again it's very important that you understand that the variable S1 over here doesn't hold the actual data but a pointer to the data which will get allocated to the Heap memory so when we allocate to the Heap we will get back a pointer which S1 is holding alongside with other metadata now because over here we are providing S1 as a reference that means the variable s from this function will then be another pointer which points to the S1 variable so s points to S1 and S1 points to the Heap allocated data and that means that S1 over here Remains the owner of the string let's see Mutual references as you can see we initialize a string and assign it to S now then we call the change function with a mutable reference and notice that you have to annotate that you want the reference to be mutable this is important and it has to be explicitly stated so that the rules are set in place because remember you can only have one single mutable reference at a time and then this change function over here as you can see in the function signature it takes an argument of type mutable reference to string which we are providing right we are providing a multiple reference to S which holds a string and then we can manipulate the string now again very important s Remains the owner of this data even though we are manipulating it in this function over here s Remains the owner because we are here dealing with references so let's look at some examples just to strengthen the knowledge of this concept as you can see we here initialize s with a string and then we have here two mutable references to S and we assign it to R1 and R2 now this would fail this program doesn't compile now this would violate the first rule of borrowing which says that we can only have one mutable reference to the same data at a time but what we can actually do let's see this program as you can see again we have a variable s which holds a string and notice over here we have an inner scope so in this scope R1 will hold a mutable reference to s now because at the end of the scope R1 we go out of scope meaning it will be dropped we can then again pass a mutable reference to R2 so this program will compile so over here again initializing s with a string here we have two immutable references to S now up to this point no problem because the first rule states that we can have any number of immutable references the problem is over here because we are then assigning immutable reference to S to R3 this is a problem because this would violate the first rule of borrowing which says that we can either have any number of immutable references or one single mutable reference but we can't have both of them immutable and mutable references at the same time now over here as you can see we have the exactly same program but as you can see we are here having two immutable references assigned to R1 and R2 and then we just print it out now after this point over here the variables R1 and R2 won't be used in the entire program and that means that we can assign then a mutable reference to R3 it's important to notice here that again we don't use R1 and R2 after this point so it's allowed to have a mutable reference over here while over here as you can see we are trying to access all of them at once this would cause a problem now I've showed you some of the reasons why the compiler is so strict and one of them was dangling references so let's see what a dangling reference is as you can see over here we are calling a function dangle now in tangle we are initializing a variable with a string type and then we are returning over here a reference to this string as you can see in the return type it is a reference to a string now the problem here is that when the function returns this variable over here will hold a reference to this string right and s over here will get out of scope at the end of this function scope here and that means this variable over here will point to something that has already been dropped or deleted so it points to a garbage value right and rust won't allow you to compile a program like that because this will violate the second rule which states that references must always be valid very important and to avoid that we can change this tangle function over here and just returning this string itself right so reference to nothing would then be the owner of the string but returning a reference to something that has been declared inside the function scope is a bad idea all right let's do some exercises as you can see we have here a variable holding an i32 value then over here fill the blank and we have to complete this P assignment over here now let me see over here the memory address of X is and we are providing P so we want to print out here the memory address and by the way this is denotation if we pass here a pointer that it actually outputs the address of the memory location that P is holding so over here what we want to do is we want a reference to X now as I've told you P will then be of type reference to an i32 right because X is holding an i32 type now a reference to an i32 is basically a pointer to a location in memory that points to this data over here so P here holds a memory address we'll see that when we execute this program as you can see the memory address of X is this so this memory address is where the value 5 is stored in in memory and we are accessing that using a pointer over here again a pointer is just a normal variable which holds a memory address in this case the memory address of x right so this is a reference to X over here again we have variable X holding i32 type and a reference to X which is a reference to an i32 value modify this line again we compare here y with five now y doesn't actually hold the value 5 it holds a memory address now to access the data at which this y pointer here will point to we have to dereference and we do that with the reference operator like that that means go to the location of Y and give me the value right so when we go to the location where X is stored for example at this memory address we want to get the value that is stored at that address we are using here the star operator just to access the value so over here we initialize a variable holding a string type then we are calling the function borrow object and as you can see the borrow object function expects an argument of type reference to a string but what we are here providing is a string right we are providing s which is of type string now we want here to pass a reference to s meaning a reference to string in this case we are complying with the function signature over here again variable s holding a string then we call puster function over here now again we have to always take care of the function signature what is expected so as you can see the argument should be of type mutable reference to a string so we have to pass here a mutual reference to s where s is holding a string right number five again here having a string and as you can see over here we are mutating p so that means we want to have immutable reference to s as you can see p will then hold P will then hold immutable reference to a string type right and then we can mutate the string that P is holding and again because we are here having a reference S will remain the owner of the data meaning we still can use S even after this assignment over here ref can be used to take references to a value similar to ampersand as you can see we have here a variable holding HR and this over here would be a variable which holds a reference to a chart right a reference to C now fill the blank don't change order code now instead of here using the Ampersand symbol we can use the ref keyword and it's exactly the same actually they are very similar there are some minor differences in pattern matching um but we will come to that so when we dereference R1 and R2 then this should hold the same data right and over here check the quality of the two address strings so we have here a function get address which gets called with R1 and R2 now all this function over here does is it takes a reference to HR as an argument and it will then put it inside this format macro now this format macro over here is the same as the print line macro with the difference that the print line will print to the standard output while format will return a string right so we take this argument and then we will return here the memory address of this reference now in this case R1 and R2 should hold actually the same memory address right because they are pointing to the same data this case C so R1 is pointing to C and R2 is pointing to C all right so let's see and this is compiling borrowing rules remove something to make it work don't remove a whole line again here having immutable variable s which holds a string then we have two mutable references to s and this actually would failed to compile because again we can only have one mutable reference at a time so to solve this as you can see over here we are just printing out R1 and R2 so this reference over here don't need to be mutable right because it's sufficient that we have a read-only reference to S and again the rule states that we can have as many immutable references as we want so this is compiling and also just to show you when dealing with references over here as you can see s is still valid right error borrow an immutable object as mutable fix the error by modifying this line as you can see we have here a string and we call here borrow object with a mutual reference to S so the problem over here is that we can't pass a mutual reference to something which is immutable so we have to make this variable over here mutable and then we can uh then we can provide here a mutable reference to this variable but the other way around it's no problem okay borrow a mutual object as immutable so this code has no errors as you can see we have here a string and we are passing to the borrow object and immutable reference right now this would actually compile because as you can see the function signature States it should be an immutable reference even though s itself is mutable we can still pass it as an immutable reference this would compile without problems and then over here you can see we are even mutating s because it's mutable comment one line to make it work we have here a string and we have a mutable reference to S right so this would be the type annotation mutable reference to a string type now we are then mutating R1 and over here we again have a mutable reference to s and then we are modifying R2 the problem here is that you know the rule that we can only have one mutable reference at a time but as you can see R1 over here gets used again so this should remain valid but we can't have two valid mutable references at the same time so if I comment this out then this program should compile because we are not using R1 after this point meaning we can have another mutable reference and even if I print out here R2 this should also compile because over here the only valid mutable reference to S would be R2 because we are not using R1 after this point so over here again we have a string and we have two mutable references to S now if we would run that it would actually compile as you can see the program compiles even though we have two mutable references to S but we are not using any one of these in the program but over here it says add one line below to make a compiler error cannot borrow S as mutable more than once at a time you can't use R1 and R2 at the same time so let's use both of them over here to force this compiler error and as you can see this would be the compiler error we are expecting Canon to borrow S as mutable more than once at a time and over here you can see really the Rus compiler is one of the best compilers I've ever seen because it provides you with really useful compiler messages alright see you in the next topic so we have now reached the topic of compound types now a compound type is a type which is made up of other types for example a string is made up of characters so it's considered a compound type now we have in Rust two types of strings we have string with a capital S string like that and we have string slices now if I say string I will always refer to this type otherwise I will say string slice so a string is a heap allocated string type that owns its contents and is mutable we have seen that a string is allocated in Heap memory a string slice is an immutable sequence of utf-8 bytes in memory it does not own the underlying data and is immutable so think of string slice as a view on a sequence of characters stored as utf-8 bytes in memory it's basically just read only so use string slice if you just want to view a string and string slice is more lightweight and efficient than string and U string if you need to own the data and be able to mutate it then we have string literals a string literal is a sequence of characters enclosed in double quotes its fixed size compile time known sequence of utf-8 bytes the type is reference to static string which indicates the data is stored in static storage meaning it is valid throughout the entire lifetime of the program the data is hard-coded into the executable and stored in read-only memory meaning they are immutable so let's now see an example of a string slice and I'm seeing my head is in the way here let's do it like that so let's see over here we are initializing s with a string now again I repeat myself I know but this is very important s doesn't hold the actual data it holds a pointer which points to the data that is that is allocated in Heap memory so s will have a pointer alongside other metadata that will point to the first character of the string and let's now consider this world variable here so we are taking a reference to s meaning s will remain the owner of the Heap allocated string so we are taking a reference to S and providing here an offset from 6 to 11. now I hope you remember in a Range this would be excluded so it would be from 6 to 10. so when we look at the world variable we see that it has a pointer and a length now this length over here is actually false I'm sorry for that this should be the length of the string slice in this case one two three four five so this pointer points to the offset of the string we have provided so it points to index six and it goes to 10 because again 11 is excluded so this world here will hold a string slice of this word right so the string slice World points to a sequence of characters stored on the Heap this is basically just a view into a heap allocated string and that's why it's immutable all right the type of string literal hello world is Ampersand stir for example let s string slice equals hello world now this would be a string literal right because we are hard coding it into the executable and a string literal is also considered a string slice because it's also immutable and it's basically just a reference to the static memory this string gets stored in so store and string slice we can't use Stir type in normal ways but we can use reference to stir fix error without adding new line as you can see this would over here be a string literal we are hard coding this string into the executable into the program itself so again we can't use Stir as a type but we can use a reference to a stir right we can only use Stir by boxing it Ampersand can be used to convert Boxster to string slice now fix the arrow with at least two solutions as you can see over here we have a string literal a string hard coded into the program itself then we call the into method we will cover that later but basically into will convert a type into the type we are annotating here so this string literal will be put into a box meaning it will be Heap allocated then we are calling this greetings function with an argument s and as you can see as this argument over here is has to be of string slice so what we can do over here to convert this box into a string slice is passing a reference to this box as you can see this is compiling but we even can just omit all of this right this photo would be string slice and we then just pass s notice that we don't have to pass a reference to S because s is already the type we need string type is defined in standard library and stored as a vector of bytes back but guaranteed to always be a valid utf-8 sequence string is Heap allocated growable and not null terminated so let's see we want to initialize here s with a string right then we are mutating this string over here and mutate it again now over here we are pushing to it a string literal and over here we are pushing to it a single chart now if you push a single chart you just use the push method not not the push stir method and in the end as should hold hello world now we can initialize here an empty string like that this is now an empty string type now why are we using this type over here and not string slice like so for example why aren't we use why aren't we doing it like that because remember a string slice and string literals are immutable they are just references to some string data in the memory so we have to use string here so we are here initiating an empty string and then we are mutating it and push to its held over and this exclamation mark this compiles let's output the string as you can see the string over here has been mutated and that's the whole point of using this string type over here it can be mutated they can grow and shrink and so on so let's see exercise four fix all arrows without adding new line as you can see over here we have initialized a variable with a string then we are mutating this string over here which means we have to make this variable s here mutable so we are pushing A Single Character this comma and then we are pushing another string like that and we can even push to the string by using this notation here even though I don't like it but it's also possible but over here we have two pass it a string literal because this is defined in this standard Library all right let's see and of course over here sorry we have to use the push stir method as you can see this is compiling so again we are pushing A Single Character then we are using here the push stir method pushing a string literal and we can also mutate strings like that we place can be used to replace substring again we have here a string and we then can replace some elements inside the string so we provide as first argument the element we want to replace in this case dogs and it should be replaced with cats so S1 should then hold I like cats now again replace will mutate the string meaning the S should be mutable foreign methods can be found under string module so number six you can only concat a stream with a string slice and strings ownership can be moved to another variable so we have here S1 holding a string and S2 also holding a string now as you can see we want to concatenate these two strings and we can use the plus operator for that now in the rust documentation it says that the first argument should be of type string while all other arguments must be string slices so we have to convert S2 over here to a string slice now there are two possibilities the first one is using the s store method like that so this over here will then be considered as a string slice so we have converted a string to a string slice right but the output but the result of this concatenation will be of type string it's important to remember that and S3 should then hold this concatenated strings now because S1 over here has been moved inside S3 we can't use S1 anymore because S3 has become the owner of this data so let's instead print out S3 and as you can see this is compiling now there is an easier approach to that we can convert a string to a string slice by just by just providing a reference to a string so a reference to a string can be inferred to a string slice like that string slice and string opposite to the seldom using of stir string slice and string are used everywhere swing slice can be converted to string in two ways so we have covered that fixed arrow with at least two solutions so over here we have a string slice right now we call the greetings function over here with an argument of s now s should actually be of type string so we can convert that by calling this two string method like that so we have converted here a string slice to a string all right the second way would be doing it like that string from and then providing s as you as you can see this is compiling and there are other ways for example two ohms even though I think this is not used that much because it's more General but it's also possible right we can use string form or two string to convert a string slice to a string use two approaches to fix the arrow and without adding a new line as you can see we have here a string literal that has been put into a string so this variable over here will hold a type of string now to convert this s over here into a string slice because this is the type annotation of this variable we can as I did before we can pass as as a reference and again a reference to a string will be inferred to a string slice again we can use as stir for example would be the same string escapes you can use escapes to ride bytes by their hexadecimal values fill the blank below to show I'm writing rust so as you can see we can escape certain values and they will then be convert to ASCII characters for example this would be the hexadecimal representation of the ASCII value of s right so if we want to write rust here then the next character to S would be T so we would write X 74. like that and you can check this out over here there are a lot of examples where we can just Escape using the backslash Now using two backslashes here will print out this literal and these literal characters so the output will be backslash x3f okay because we are basically escaping this backslash all right and over here we have raw string and a raw string is a string in which there are no escapes so everything is printed literally as it is written so the output of this let me run that so the output of this would be backslash x3f it won't be converted to an ASCII character so let's see string index you can't use index to access HR in a string but you can use slice reference to a S1 for example and the offset so let's see we have here a string and now we want to access the first character now in most programming languages this would be possible but in Rust you have to use string slices meaning you take a reference to S1 and then provide the offset if we want to only access the first character we would provide an offset in form of a range from 0 to 1. now again in a Range this would be excluded meaning we only access this first element so the return type will then be a string slice notice something here when we assert age we would have a string of H and not a character even though we are here only accessing a character we are accessing it using these string slice this string slice notation over here so remember a string slice is just a view into a heap allocated string right so we are just looking what is at this location in this case we just want to access the first location over here right modify this line to fix the error tips this would take three bytes in utf-8 format and that's right we have said that a character in Rust is of size 4 bytes now a character in a string is a little bit different so if it is an ASCII a character then it will usually take one byte and DC Unicode characters can take three bytes or I guess some of them even four bytes all right so we want to have in H1 only this character so again we are taking a reference to S1 and providing an offset oh 8 would be at index 0 I would be at index 1 this comma index 2 and then this will be index 3. now this takes up three bytes so we have to provide here an offset from three to six remember six is excluded so it goes from three to five three four five three bytes let me output that as you can see this would be the output and again this is just a view into a heap allocated string right operate on utf-8 string fill the blank to print each character in this string so over here there is a method called chores now chores will put this sequence of characters into an iterator meaning we can then iterate over it using a for Loop for example and that means c will then be in each iteration one of these characters and we will then just find out C as you can see in each iteration we are printing out a single character alright that's it see you in the next topic so we'll Now cover arrays now an array is a fixed size collection of elements of the same data types stored as contiguous Block in stack memory now it's stored in stack memory because its size is known at compiled time the signature of an array is like that you can see over here which indicates that the length is fixed at compile time arrays can neither grow nor shrink they must retain their size so let's see the type of array is like this as you can see erase length is part of their type signature so their length must be known at compiled time for example you can't initialize an array like below this would not be possible as you can see we have an init R function which takes an argument of type i32 and then we try over here to initialize an array with the length that is provided as an argument this would be and compiler error because as we've seen the size of an array must be known at compiled time if we do it like that the size of the array would only be known at runtime this will cause an error because the compiler has no idea of the exact size of the array at compile time let's see fill the blank with proper array type as you can see we have here an array with five elements now what would be the type of these elements it would be the default integer type which is i32 so we annotate arrays like that in square brackets then we would have to type of the elements and the amount of elements inside of the array which is five modify the code below to make it work so we can call the Len method on an array and it will then provide the length of the array so in this case this would be 5 right because there are five elements inside number two we can ignore parts of the array type or even the whole type let the compiler infer it for us so we actually don't have to annotate the type or we can annotate the type but leave for example the type annotation here for the compiler to infer fill the blank arrays or stack allocated size of well returns to bytes which in Array occupies HR takes four bytes in Rust Unicode Char so we have seen that before the size of well we'll just take an argument and it will output the size in bytes the provided argument occupies in memory so if we provide the r over here then we should actually get back 3 times 4 bytes because because a character in Rust takes four bytes meaning this array over here would occupy 12 bytes in memory all elements in an array can be initialized to the same value at once so as you can see we have here an array which should hold 100 elements and each element should hold the number one okay we are here accessing the first element and this is the notation for accessing elements in an array in this case we access the first element at index 0. until length would be 100 so instead of typing out 100 times 1 we have a short 10 Syntax for that so we can here first of all provide the element it should hold in this case one and we want it to hold 100 elements of this value over here number four all elements in an array must be of the same type so as you can see because when we annotate an array we actually have to define the type here in this case we want integer types and we have three of them now as you can see this is a torrent this is not possible all elements inside an array must be of the same type indexing starts at zero so over here we have an array of characters let's annotate that and there are three elements inside it and now we want to access the first element right we want this variable over here to hold the first element now again arrays our zero indexed meaning we access it using this notation here and provided a value of 0. out of bounds indexing causes panic so over here we have an array which holds two strings and we want to access the first element right now we can use this notation over here like we did before or we could use the get method the difference is that get will return an option type we'll cover that in a later episode but it's generally safer than using this notation and this is especially true if you're accessing elements at runtime okay so we can use this notation or we could use this notation now over here this would panic because this array has only two elements meaning indexes 0 and 1. so let's access here index one and this compiles so let's see slices a slice is a reference to a contiguous sequence of elements in a collection it provides a way to borrow part of a collection without taking ownership of the entire collection it can be created from arrays vectors strings and other collections implementing DT ref trade so let's see this example of a slice we have here an array and then we are over here taking a reference to a and providing an offset essentially the same thing we did with string slices right a slice is just a view into a collection and over here we try to access the elements at index 1 to index two right because 3 would be excluded so the variable slice will then hold a slice with the elements 2 and 3 in it all right let's see slices are similar to arrays but their length is not known at compiled time so you can't use slice directly here both this slice over here and store or slice types but directly using it will cause errors you have to use the reference of this slice instead so because these two slices over here their sizes are not known at compile time we have to use a pointer to that right and we have seen that in the example of string slices so we use this syntax over here fix the arrows don't add new lines as you can see we are holding here an array let's annotate the type like that this is an array of three i32 integers now we want here to get a slice with this offset meaning from 0 to 1 0 1. so again like we did with a string slice we want a reference to an array with this specific offset so we are trying to access 0 to 1 because 2 is X because 2 is excluded meaning we are accessing here index 0 and 1. the elements one and two so this slice over here will then hold one and two right now the type annotation here would be like that because we are holding a reference to this array right and this would be the type annotation of a slice now over here we have a string later on now string literals as we've seen always are annotated like that string literal is hard coded into the program's binary and that's why we can access it using a reference to this string literal over here which is in the binary itself all right a slice reference is a two word object for Simplicity Reasons from now on we will use slice instead of slice reference the first word is a pointer to the data and the second word is the length of this slice the word size is the same as U size determined by the processor architecture for example 64 bits on an x8664 slices can be used to borrow a section of an array and have the type signature like this where T over here stands for the type as you can see we have here an array with three characters now we want to have a slice of this array with the first two elements these two elements and this is shorthand notation instead of writing it like that we can just omit the zero all right so we are accessing 0 1 and 2 is excluded so the slice variable should hold a slice with these elements the type annotation over here would be reference to char okay modify 8 to make it work tips slice reference is not an array if it is an array then assert will be packed each of the two characters this and this occupies four bytes 2 times 4 8. now because this slice variable over here doesn't actually hold an array but a slice we are here actually holding a pointer right because this is a reference to this array and that means we have seen over here A Slice reference is a two word object meaning the pointer and the length field will both occupy sizes of Type U size and your size if you're on a 64-bit computer it means you size will be of size 8 bytes or 64 bits and that's why a slice holds 16 bytes right if we would pass the array here we would have if this slice would have been an array then 8 would actually pass because we would have two characters each character is four bytes meaning eight bytes but because we have a slice which holds a pointer and a length both of Type U size we have 16. so over here we have an array of five integers of type i32 fill the blanks to make the code work as you can see we want the slice variable to hold a slice of these elements so let's first annotate the type actually so this slice variable over here should hold a slice of I 32 elements right so we are taking a reference to the array and provided an offset we want to go from this to this meaning index 0 1 we are starting at 1 and we go up to 0 1 2 3 4. and I choose 4 here because 4 is excluded so we will go from 0 1 2 3 to index 3. string slices and we've covered that already but let's do some exercises for repetition now over here we have a string and we take over here a string slice meaning we take only the first two characters like that or let me write like like that right we take the first two characters fill the blank to make the code work don't use 0 to 2 again so over here we have a second variable which should hold a string slice and they should actually hold the same elements as you can see over here we are we are here asserting that slice one and slice two are the same so instead of using this syntax over here as we've seen before we can use like that we can omit t0 over here we have a string literal again a string literal is hardcoded in 2D binary and this would be the type annotation modify this line to make the code work as you can see we want slice over here to hold a string slice with only this character so let's annotate the type this would be a string slice and because this Unicode character over here takes three bytes we have to provide an offset here of three meaning zero one two right three bytes alright last one number six a reference to a string can be implicitly converted into a string slice now over here we initialize a variable s which holds a string here reference to S is reference to string type but first word needs aced but first word needs a string slice as you can see over here it works because reference to string can be implicitly converted to string slice so as you can see over here we are calling the first word function which takes us an argument a type of string slice and this would actually work because we are here providing a reference to S which means this over here is a reference to a string right and a reference to a string can be converted to a string slice implicitly by the compiler so then this function over here will take a reference of s and provides an offset from 0 to 1 where 1 is excluded so we just so we just take the first element and it returns this as a string slice that means that the return value of this function will be assigned to the word variable then over here we are calling the clear method on S which means this string will then be empty clear will just remove every content in the string the string itself will still remain in memory with the same capacity and S also is valid so it's different from drop but it will empty the string essentially so we will then have over here an empty string the problem over here is that clear takes immutable reference to self and that's a problem because over here we have a reference which is immutable to S and we have over here immutable reference to us now the problem arises because we are here using the immutable reference so if we would delete that and put it over here then after this point the word variable over here which holds an immutable reference is not used anymore and so we can use this clear method and this is compiling and as you can see we just get the first element alright see you in the next topic now we will cover tuples we have seen some tuples before and some exercises but now we will go into greater detail so a tuple is a way to store related pieces of information in a single variable and it's essentially a collection of values of different types grouped together as a single compound value again a compound value is a type composed of other types it's stored as a fixed size contiguous block of memory on this stack and the signature is parentheses and the types the Tuple is holding let's see elements in a couple can have different types pupils type signature is like that where T1 T2 are the types of tuples members so as you can see we can hold different types we have here an u8 and an i16 together in a tuple tuples can be tuples members so we can even Nest tuples so this Tuple over here as you can see holds an u8 over here and another Tuple inside it which holds an i16 and a u32 integer all right fill the blanks to make the code work so over here we have to annotate the type now again the first element over here would be u8 the second one is u16 the third one is an I64 the fourth one would be a string literal and the last one is a string members can be extracted from the Tuple using indexing so as you can see we have here a tuple let's annotate the type this is how we would annotate a tuple and it holds three string literals then we are accessing here the second element tuples remember are zero indexed but in this case it would be M but we want it to equal sulfates so we have to access d index 2 right 0 1 2. long tuples cannot be printed so if a tuple exceeds a certain amount of elements it is too long to be printed as you can see this would be too long if I delete that over here so this would work so we can have up to 12 elements in a tuple to make it printable if it exceeds 12 elements you can still use it but it's not printable the structuring Tuple with pattern so let's first annotate the type of this Tuple here we have an i32 the default integer type then we have a float which means f64 the default floating Point type and we have a string literal fill the blank to make the code work so over here we want to destructure this Tuple where X holds one y holds hello and that holds 6.4 so let's see re-distructure tuples like this using the let keyword and the parentheses then the first element should be X right now the second element 6.4 should be put into Z and the last element should be destructured into variable y the structural assignments over here we are declaring some variables and then over here we destructure this Tuple and we want to put it inside these variables so we can so because we have already declared these variables we don't need to declare it once more so we can omit the LED keyword and then all we do is we destructure it so one should be put inside variable y 2 should be put inside variable Z and 3 should be put inside variable X tuples can be used as function arguments and return values fill the blank need a few computations here so so as you can see we are calling this function some multiply and this function over here expects an argument of type Tuple and the Tuple should hold two integer values what we do over here is we perform some operations and return it in a tuple right so so the first operation will be this one adding the first element of the provided Tuple with the second element of the provided Tuple and then we are multiplying the first element of the provided Tuple with this second element of the provided Tuple and we return that in a tuple again so the result of this operation over here will be the first element and the result of this operation will be the second element and as you can see we and as you can see we can return that in a tuple now after returning this Tuple we destructure it so the first element will be put in X and the second element will be the structured to the variable Y where X will hold 5 and Y will hold 6. so we have now to provide here two numbers when we add them together they will equal they will evaluate to five if we multiply the two numbers they will evaluate two six so this is a little riddle for you and try to solve it and the answer would be 2 3 right and don't forget to add here parentheses because we are passing it here a tuple right we are not passing two arguments we are passing one argument which is a tuple that holds two elements very important so if we add 2 and 3 we will get 5 and if we multiply two times three we will get six and this will be the return type of this Tuple over here all right see you in the next topic up until now we have been using types that are defined in this standard library but we can in fact create our own types and we can do that by using a struct now a struct is a compound type allowing to group together values of different types into a named data structure it's similar to tuples but each value has a name so the values can then be accessed through this name and a struct has to be instantiated with data think of it like destruct itself is the template for the instances you create from it now let's see how that works as you can see over here we have our template in this case a struct named user and the user struct holds various fields and we can define a field by giving it a name and a type that this field should hold so in this case the active field holds a type of Boolean username holds a type of string email string and sign in count u64 and then we can instantiate this struct and we do that by calling the name of destruct user in this case and providing for each field a concrete type and of course the types have to match the template then we are assigning this instance we have created over here to a variable so this variable will then hold an instance of the user struct and we can access and even mutate the fields of an instance so over here as you can see we have created an instance and assigned it to the user1 variable and then we can use this syntax over here to access a field so in this case we are even mutating the email field from the instance user 1. as you can see we do that exactly like we would mutate a variable just assigning it a new value now to mutate you have to make it explicitly mutable meaning you use the mute keyword here like you did with variables and functions can also create instances and return it so in this case we have the build user function which creates an instance of the user struct and as you can see again it's just providing concrete values for each of its fields and over here as you can see this function takes two arguments email of type string and username of type string and then it provides it 2D username field and to the email field now in this case we are repeating ourselves as you can see and we can avoid this by just using this shorthand syntax so if the name of a field and the name of a variable or argument matches then we can write it like that and this just makes it less verbose and there is also a concept called struct update syntax so over here we have an instance of the user struct now we want to create another instance but taking some of the values from the fields from user 1. as you can see for example in this instance we want the active field to hold the value of user 1's active field so we are accessing user 1 and the active field and as you can see we are taking everything from this user one instance except the email and as I've said there is a better way to write this and this would be instantiating a struct like we did before but just providing the fields that are different from the instance we we want to take from it so for example over here we say that the email field should hold this value and the remaining values should be taken from this user 1 instance so this will then create a user instance with all the same values from user 1 except the email field then we also have Tuple structs and Tuple structs are like normal structs but using tuple-like Syntax for defining their fields it's basically a named Tuple and it's instantiated by parentheses instead of curly braces and its values are accessed using Point notation and then we have unit-like structs and these are structs without any Fields meaning they don't hold any values and it's mainly used when working with trades and this will be covered soon and that means they don't store any data let's solve some exercises the types of structs exercise one we must specify concrete values for each of the fields in destruct as you can see we have here a person struct holding various fields and then over here we have a variable H which holds a value of 30. now then we are instantiating this person's struct and assign it to the variable P meaning P will then be an instance of the person's struct now as you can see we have here the fields and the concrete values and this would be the shorthand Syntax for this because we have over here a variable matching the name of the field so we can write it like that but as you've noticed we didn't provide the hobby field a concrete value so when you are instantiating a struct you have to provide to each field a concrete value so let's do that and the variable P will then hold a type of person now h of course has to match with the templates so this would be an u8 and of course we use commas here and not say me colons all right unit struct doesn't have any field it can be useful when you need to implement a trade on some type but don't have any data that you want to store in the type itself so as you can see this example is using trades and we have and we have not yet covered that so I will skip that because this might just be confusing and we will cover that soon number three Tuple truck looks similar to tuples it has added meaning this track name provides but has no named fields it's useful when you want to give the whole Tuple a name but don't care about the fields names so as you can see over here we have two Tuple structs one color and one point and they are exactly like tuples but they are named okay so over here we have three integer values in the color Tuple struct and in the points rack the same thing now we want to instantiate the point struct over here and the variable V will then hold an instance of our custom type point then we are calling the check color function and provide an argument of B now as you can see the check color function takes an argument of type color but this is not what we want right because we are here passing a variable of type point so let's change that and over here we are then destructuring the instance that was passed so in this case as you can see X should hold zero so let's provide this value over here then the P1 field and as you can see we are accessing the fields in the Tuple struct like we would access elements in a normal Tuple using this syntax so over here we are accessing the second element and this should hold 127. so that means the last element should hold 255. and as you can see over here we are destructuring so the first element over here gets this structured into a variable X now the second one over here we don't need to destructure right because we are accessing it directly using the PE instance and the last one let's call this Z okay and by the way when destructuring and you don't need a value like for example this one you just provided an underscore let's see and of course when the structuring we have to provide the name of this truck in this case point and this is compiling operating on structs you can make a whole struct mutable when instantiating it but rust doesn't allow us to Mark only certain Fields as mutable fill the blank and fix the arrow without adding removing new line again we have the person struct we have here the age variable let's annotate that and we have the P instance so as you can see we want to mutate the age field of the P instance and this is right but the problem is the P instance is not mutable so let's make that mutable and then we can also mutate the name field like that using field initial shortened syntax to reduce repetitions so again we have the person struct and in main we are just printing out something and over here we have the build person function so as you can see this function takes two arguments name of type string and H of type u8 and these arguments exactly match the types defined in the template so then we can over here instantiate a person struct meaning we can hear for the name field provide this argument and we have seen we can use field image for 10 syntax meaning if the field name matches the name of a variable or an argument we can skip it like that let's see exercise 6 you can create an instance from another instance with struct update syntax so as you can see we have here the user struct and we have seen an example of that in these slides then over here we are creating an instance from the user struct that means the variable U1 will then hold an instance that means that the variable U1 will then be an instance of the user type and again we are just providing each field over here with a concrete value then over here we call the set email function with an argument of the U1 instance so let's see over here as you can see in the function signature the argument must be of type user and then we want to create over here a new instance and what we want to achieve here is that we take everything and what we want to achieve here is that we take every field from the U1 instance except for the email field we want to we want that to be different so what we can do is providing a concrete value for the email field and then just saying the rest we want to take from the argument provided and then we are just returning it so that means U2 will then hold a newly created instance Point distracts we can use derive debug to make a struct printable click fill the blanks to make the code work so you actually can't print out a struct using the normal print line syntax we have to so to be able to print a struct we have to derive a trade again we will cover trades but we have to derive that using an attribute all right that means that destruct rectangle here implements this debug trade and that means we can then use debug notation to print it out so over here as you can see we have a variable scale and then we are creating here a rectangle instance so we are providing a concrete value for the field with and this would be 30 times the scale and we provide a concrete value for height now as you can see print debug info to standard error and assign the value of 30 times scale to width now this debug macro over here will just print out debug information to the standard error output and that means over here rect one will then hold an instance of the type rectangle and the type annotation for the scale variable over here would be u32 right because we are passing this scale variable over here meaning it should be of Type U 32. and then again we are here using the debug macro and printing out racked one and instead of doing that we can also use a normal print line statement like you're used to except that in the placeholder over here we have to use this notation and this is called debug notation meaning any type that implements the debug trade can then be printed and the difference between debug macro and printland macro as you can see print line will point to the standard output while debug will print to the standard error and that's why you don't see it over here you just see the print line output now to see that I will go to the rust playground and we can just run this code and as you can see over here this would be the debug output so this debug macro over here prints out the exact thing we are passing to the width field in this case 60. and this debug macro over here points out the instance right we then have an rectangle with a width of 60 and a height of 50. basically the same thing that we print out with the print line statement here foreign move within the destructuring of a single variable both by move and by reference pattern bindings can be used at the same time doing this will result in a partial move of the variable which means that parts of the variable will be moved while other parts stay in such a case the parent variable cannot be used afterwards as a whole however the parts that are only referenced and not moved can still be used and I guess we have seen that before but let's do another exercise so as you can see right we have seen that alright so let's move to the exercises directly because I've already covered that and I think the exercises are better for understanding so as you can see fix the arrows to make it work over here we have a file struct holding two fields of type string then in main we are creating an instance of the filed struct providing it concrete values for each field meaning F will then hold an instance of the file struct and over here we are then assigning the name field of the F instance to the underscore name variable so what would be the type annotation here it would be string right because F name holds a string and that means the name variable here will become the owner of this string data and that is the reason that we then cannot use f as a whole and also not F dot name because a partial move happened so if a field gets moved out to another owner then you can't access this field and also not the data structure that is holding this field so this would fail over here and this would fail now to fix that we can actually access just the name variable here right because this is now the owner of this string but what we can't do is accessing the whole instance over here this is not possible as you can see this is working now if you actually want to keep f as the owner of all the fields and to print it out like that and by the way notice again when we are outputting a single field we used in normal notation if we output the whole instance then we have to use debug notation because these fields over here hold types that are defined in the standard Library while this is a custom type we have created so to make that work we can also use here the Clone method and as you can see this is working and we are here printing out the instance all right see you in the next topic we can also create custom types using enums now an enum is a way of defining a type with only one of a possible set of values we can only access one variant of an enum at a time and it can hold additional information using tuples and it's especially useful when using in match statements let's see an example of an enum so we have here the enum IP address and this enum holds two variance so in enums we call it variance and not Fields like instructs and each variant over here holds a type using tuple-like syntax all right so an IP address by the way can be in either one of two formats either it's V4 or B6 and now we will see the difference between enums and structs so this is how we would instantiate this enum IP address we are accessing the name of the enum double colon and then the name of the variant in this case V4 then providing it a concrete value I hope you noticed the difference in a struct we would have provided concrete values for each field in an enum we instantiate the enum only with one single variant all right so this home variable will then hold an instance of the IP address enum and this loopback variable over here will also hold an instance of IP address but using the variant V6 and by the way this is the old format of IP addresses ntcc new one so let's do some exercises enums can be created with an explicit discriminator so enums are enumerated right that's why they are called enum now if you don't provide here a value it will be implicitly starting from zero this would be one and this would be two and we can also enumerate them with an explicit discriminator okay so this will then hold zero this variant will be one and this will be two and over here you can see C like enum now you can't use as a discriminator a floating point value this is not allowed but like in C if you for example would do it like that we are here providing the discriminator 5. then 1 and 2 implicitly will be six and seven right or we could say this should be 9 for example right so you can manipulate the enumeration of an enum so let's see actually let me change that back and let me change that to integers an inner variant can be converted to an integer by the as keyword so when we take this enum over here and we create an instance as we've said as we've seen calling the name of the enum and the variant and then we can convert it to an u8 for example right and we can do the same thing here and over here should be the same thing and as you can see this is compiling let me print out as you can see when we access from the number in on the variant one then A1 will get outputted now let's see if I provided a value of 10 over here but for that I have to comment that out as you can see this foot would then be the discriminator for the one variant all right each enum variant can hold its own data so as you can see we have here an enum message now this enum has different variants so this variant quit doesn't hold any additional data but while this variant move here holds Fields it's very struct like and this variant right is tube alike so it holds a value in a tuple syntax and the same thing for change color so we can then instantiate the move variant from the message enum with these values over here X1 and Y 2. and as you can see because this is very struck like you instantiate it basically like a struct and over here instantiating with hello world as you can see this variant over here holds a string type so like that and please notice over here that both of these variables here hold the same type message because this is also a custom type A type we have created we can get the data which an enum variant is holding by pattern matching so over here we have the same enum like we saw before and over here we have an instance of team move variant now we can here use E flat and we will cover that in the next section two get out the data inside this move variant so we are matching here the message variable holding an instance of the move variant and then over here we can basically destructure it so X should then be destructured into a variable a and Y should be restructured into a variable B and then as you can see we can use these two variables now we are here asserting A and B and a stands for this value and b stands for this value and as you notice they are not the same so this would fail actually so let me change that and now let's run the program fill in the blank and fix the errors again same enum and over here we have an array of different variants from this enum type right we have the quit variant the move variant and the change color variant now the neat thing here is that all of these instances are of the same type message right our custom type now I hope you remember how we annotate an array so first thing will be the type and then the number of elements Let's see we have over here three elements right and the type would be I hope you know it message right all of these variants or instances hold the same type and then over here we are looping over this array here meaning message this variable will hold in each iteration one of these instances and then passing it to the show message function and as you can see when we want to take an instance of this inner message we can just use the type message here because again all of them all because all of them have the same type and all we do then is just printing it out and again because this is a custom type we cannot print it out just like that so we have to use debug notation here and derive over here the debug trait and as you can see in each iteration over here we call the show message function and it will print out the contents of this array so let's see exercise five since there is no null in Rust we have to use enum option to deal with the cases when the value is absent and I want to show you some slides for that so the option enum option is an enum that represents a value that may or may not be present it's known in other languages as null and it's referring to the absence of a value it's used to handle cases where a function or method might fail to return a value so this is how the enum option looks like as defined by the standard Library as you can see it's an enum holding two variants none and some now this over here is a generic meaning that sum can hold any type and we'll cover generics very soon but for now again think that this can hold any type provided so let's see an example over here we are here initializing the variable 5 with D variant sum holding an integer value right and then over here we are calling the function plus 1 providing it five so let's see here as you can see the argument should be of type option and the type parameter here would be i32 now we have to provide a concrete type for this generic type parameter now again we will cover that in much greater detail but as you can see this is exactly the type we are passing to the function sum is a variant of the option enum and it is holding an i32 value and it is holding an i32 type now then we are matching this argument over here if it is holding a non we will return none if it is holding a sum then over here we are destructuring the value wrapped inside this sum variant into the variable I and then over here we are incrementing I by 1 and wrapping it in a sum because as you can see the return type over here is defined as option i32 and that means that this variable over here will then hold some 6 right because we have incremented 5 over here by one so this will hold an option type and the concrete value would be sum 6. the sum variant and a value of 6. now over here we are calling the plus one function with none meaning the argument will get matched and this will then get returned right a non and as you can see the non is also a variant of the option Ina and this i32 just stands for the sum variant if your turn if we are returning none then this type over here is still valid because the non-variant here doesn't hold any value of generic type t but again generics are a topic on its own and by the way this is exactly the same that we have over here so let's do that annotating the type so over here we have an option holding an i32 type all right and as I've said this is not necessary you can omit that because again the option type is in the prelude then we are calling the plus one function again we are providing it sum 5 which is an option and then we are matching the argument so if the argument is none then we return none it is some then we destructure the value that was passed over here and we incremented by one and return it wrapped in some because the return type has to be of type option that means over here the type annotation would be option i32 and in this case we are passing a non so a non will be returned and by the way the Returns value here would be sum 6 right because 5 plus 1 will be 6. and over here we have non-anti-type annotation would still be option I 32. now we want actually to unwrap this value inside this sum variant and we can do that by using IF let so as you can see we are matching these six variables holding some six and then we can destructure it basically so we can then say sum and the variable n meaning we then can use n over here and I see right now never let this run so we have to pack that into an else block just to ensure that this doesn't run after we are running this conditional here and as you can see it will output the value inside sum now again we'll cover generics will cover a flat and match over here this will actually be the next topic so don't worry if this seems a little difficult to understand we will cover that in much greater detail Implement a linked list via enums now over here this linked list I won't cover because there are a lot of things we haven't seen so in here we have um methods and other things that we didn't really cover so I think this would be more confusing than it would be helpful what I suggest you is that you will come back after we have seen some more topics you come back to this exercise and try to solve it it's not that difficult and the solution will be on my GitHub account but I think this is just Overkill right now so see you in the next topic the normal flow of execution in a program would be from top to bottom line by line but we can actually manipulate the flow of a program and flow control is a concept that refers to the ability to control the order in which statements or instructions are executed in a program it allows to specify which instructions should be executed under which conditions and in what order so we have many ways to do that and some of them are for example using conditionals so we have the if else keywords and match for example and we can use Loops for Loops while Loops there is a special Loop in Rust called Loop we'll cover that and we can even manipulate the flow inside the loop by keywords such as continue and break so let's see so let's start with if else conditionals over here we are initializing n with a value of 5. then over here we have an if conditional checking if n is less than zero in this case this would evaluate to false meaning meaning this this instruction over here will get ignored and the program will continue executing on the next line now in the next line we have another conditional so we can use here the else if keyword and over here we are checking is n bigger than zero in this case this would evaluate to true meaning this line will get printed out now we can also match anything else using the else keyword meaning if this evaluates to false and this evaluates to false then this will get executed now in this case this evaluates to true so this will get executed meaning this over here will be ignored as you can see this was the line that has been printed out number two if else expression can be used in assignments again initializing n and over here we have a variable assignment now we can even use if else conditionals when assigning a value to a variable now remember in a variable assignment is a statement so we so we have to end that with a semicolon like that so let's see we have here an if conditional and this is a Boolean and operation do you remember we have two inputs and both of these inputs have to evaluate to true in order the end operation to return true so let's see we have here n should be less than 10 which would evaluate to True right because 5 is less than 10. and 5 is bigger than -10 so this end operation will return true meaning the meaning the code in this if conditional block will get executed and over here we are just printing out a line and then we are multiplying n by 10 and in fact we don't have a semicolon so this will be returned from this expression meaning it will then be assigned to the variable Big N so big n will then hold a type of i32 now in case this would evaluate to false then this else block would get executed meaning again we are printing out a line and then dividing n by 2.0 now as you can see we should omit this semicolon because this should because this result over here should then be assigned to this variable now we actually can't do this operation here because n is of type i32 and we are dividing it by a floating Point number so we can so we can Typecast that when I 32. and as you can see this would be the output and we are printing out big n meaning 50 over here so n has been multiplied by 10. so let's see we have here a for Loop D4 in construct can be used to iterate through an iterator for example a range A to B now as you can see we have a for Loop declaring a variable n and we provide a range now this range over here reaches from 1 to 100 and 100 here included because we have here the equal sign then we are checking if n is equal to 100 this program will panic that's not what we want so what we can do over here is just removing this equal sign because that means 100 is excluded meaning n will never be equals to 100 the largest it will get will be 99 because let's see all right fix the arrows without adding or removing lines so over here we have a so over here we have an array holding two strings now we want then to iterate over this array meaning this name variable will hold in the first iteration this string and in the second iteration this string so what we can do over here is pointing out name right so this will iterate two times and it will output this and then this now as you can see we are trying to access names again over here and that would then violate the ownership rules because as you can see in a for Loop the for Loop actually will take ownership of the provided value and we can pass here a reference like we have seen before and that means we can use names then again so and by the way we don't have to dereference name over here because the print line statement will do that automatically now over here we have three i32 integers the elements in numbers are copies so there is no move here so we are iterating over each element in this array where n will then hold in each iteration one element from this array so let's see so and this would be the output as you can see in the first iteration name will hold this string and in the second iteration it will hold this string and it prints it out in each iteration then we print out the whole array and over here the same thing but with the integers so again we have an array so iterate the indexing and value in a so there is a useful method called enumerate and this enumerate method takes a collection for example an array or a tube pillar or anything else and it will then return a tuple and this Tuple will then hold the index and T value meaning for example in the first iteration this Tuple will hold for i 0 and for V4 right because at index 0 we have a value of 4 and it will then just print out the index but we increment it by one and the actual value and of course we have to put a into an iterator so let's see like that so we have to first put a into an iterator and then we can call this enumerate method on it because the enumerate method is implemented in the iterator trade we'll take a look at that later so as you can see this will be the output and that was the reason we have incremented I by one because we don't want to start with the zeroest element is four right so we say here the first element is four second three and so on while the while keyword can be used to run a loop when a condition is true fill in the blanks to make the last print Ln work so as you can see we have here a mutual variable n Loop while the condition is true so as you can see this is a while loop and we provided a conditional meaning if this evaluates to true then this Loop will keep running until this conditional evaluates two folds so inside the while loop we have other conditionals as you can see we here check the condition and modulus 15 is 0. if that would evaluate to true then we print out that and by the way this was a question at coding interviews at Google so we are here having more conditionals and a an else conditional so meaning if anything of this fails then this will get printed out so what we can do over here checking if n is less than 10 then this Loop will keep running now when you're using while Loops then you have to ensure that the while loop eventually stops right that this conditioner over here will evaluate to false eventually because otherwise you would have an infinite Loop now to do that we can increment and over here so I increment at the end of this while loop n by 1. meaning at each meaning in each iteration in this while loop n gets incremented by one so n will eventually be bigger or equal to 10 meaning this would evaluate to false meaning we break out of this Loop continue and break use break to break the loop so we have here a for Loop looping from 0 to 100 and 100 included so if n is 66 then we want to break out of the loop anti-break keyword will immediately exit out of this for Loop so when this conditional evaluates to true we immediately exit out meaning the program will continue execution after the for Loop in this case this line over here let's see so as you can see in the end n will be equal to 66. because when I is 66 then we stop iterating continue will skip over the remaining code in current iteration and go to the next iteration so as you can see we are here again iterating from 0 to 100 and we have here an if conditional if n is not equal to 66 then we increment n by 1. and we want them to continue meaning that that the remaining code in this for Loop will get ignored so discontinue keyword the program will go back to the beginning of the for Loop now in case this evaluates to false right so if n is in fact 66 this would evaluate to false meaning we ignore this code block and we go over here where we want to break out of the loo because as you can see we want n to be 66 at the end Loop is usually used together with break or continue so Loop is a special Loop in Rust where this is basically an infinite Loop and you have to manually manipulate the flow of the loop and we will see soon so this is a u32 over here count so let's count until Infinity as you can see we are here using the loop keyword meaning this would keep looping to Infinity or we provide some keywords that will break out of the loop or continue so as you can see in each iteration we are incrementing the count variable by one now if count is equal to 3 then we print out 3 and we want to skip the rest of this iteration so we can use here the continue keyword meaning again the rest over here will be ignored the program will go back to the beginning of the loop so then over here if count is not equal to 3 this will get printed out so we print out the count in every iteration except if count is equal to 3. and then over here if count is equal to 5 then we'll print out okay that's enough and we want to break out meaning count in the end should hold a value of 5. you can see we are iterating one two and then in the third iteration this if conditional evaluates to true meaning we print out three and we continue we are going back to the beginning so the actual value of count over here is not printed as you can see there is no digit 3 but will printed out three over here because that was defined in this code block here and then add iteration 5 we will stop and break out of the loop so Loop is an expression so we can use it with break to return a value and again we have here a counter so over here we have a loop and we are in each iteration incrementing counter by one then we check if counter is equal to 10 then we want to break out but as you notice we want to assign a value to the result variable right this variable should then hold an i32 but we don't return anything here so you can Define after the break keyword a return value meaning for example we want to return counter but in this case result will hold 10 right because counter when we break out will be 10 otherwise this conditional would not evaluate to true so if counter is 10 then we break out but as you can see we want result to be 20. so we can even pass here an operation so we can double the counter value let's see and this is compiling it's possible to break or continue outer Loops when dealing with nested Loops in these cases the loops must be annotated with some label and the label must be passed to the break continuous statement so as you can see we can even Nest different loops so when we do that we have to actually provide labels so we provide here outer for the Outer Loop and inner one for the inner loop and as you can see we have even two inner Loops so we have inner one and inner two alright so let's see in the first inner loop we are checking if count is bigger than or equal to 20. if this is the case we break out of this inner loop otherwise we increment the count by two all right and notice here this would break only the inner one Loop so when we break out of this Loop this outer loop will still continue looping okay so then over here in the outer loop we are incrementing count by five and in this inner two Loop we are then checking if count is bigger than or equal to 30 then we break out of the Outer Loop otherwise if count is smaller than 30 then the outer loop will continue meaning the program will go back to the beginning of the outer loop and continue executing from here so let's go step by step and as you can see we have to find out here what the count value will be in the end okay let's go step by step we start at zero then over here we are checking is Count bigger than or equal to 20 no right so that means count will get incremented by two and this will continue because we are in this Loop remember and this will continue until this evaluates to true meaning when count is 20 then we break out of this inner loop meaning we continue executing here then count would be 20 and we increment it by 5. then we enter this inner two Loop so 25 is not bigger or not equal to 30 meaning we don't break off outer but we will continue outer meaning we go back over here now we enter this Loop here and 25 is bigger than 20 so we break out of this Inner Loop and continue here then we add 5 to count meaning count is now 30. so we enter this inner two Loop and this will now evaluate to true because count is equal to 30 meaning we break out of the outer loop so in this case count will be 30 in the end and this succeeded and I will see you in the next topic pattern match is a powerful construct that allows you to compare a value against a set of patterns and then execute different code based on which pattern matches and these patterns can be made up of literal values variable names wild cards and so on and in a match statement all possible cases must be handled and this is enforced by the compiler and really this match keyword is something that I really miss when I'm coding in other languages so once you get used to it you will love it and you will see why so let's see an example over here we have in coin enum and this enum holds variance of different denominations of U.S currency or U.S coins and here we have a function value in sense this takes as an argument a variant of type coin then it matches the argument and as you can see it matches the variance of this coin enum meaning if we provide an instance of this coin enum which holds a penny variant for example then it will return one right and this will then be the return value of this function so for each denomination over here it will return its proper value then let's see if let so I've said that in a match statement all possible cases have to be handled and this could sometimes lead to annoying boiler play code that is not really that necessary so for example we have over here an option type right a value wrapped inside sum and then we match this variable over here that holds this option type and then we are matching as you can see if it is sum then we the structure the value inside this Max variable and then we just print it out now as you can see we also have to match any other case meaning if it is not some in that case none then nothing will get executed meaning it will return a unit type now this is sometimes two verbose especially for cases like this for example you just want to unwrap something from a sum variant now what we can do instead is just using using E flat so it's exactly the same as this match statement but we know that config Max holds a sum value right so we don't have to so we don't have to consider the case where it holds a non-value and as you can see this is how we would destructure this value into the variable Max we will see a flat keyword and then the pattern so if config Max holds a sum then the value will get the structured let's do exercises fill the blanks so over here we have a direction enum holding directions over here as variance then over here as you can see we are instantiating the direction enum with a variant of the South so the type over here would be Direction then we match this variable here and if this variable over here holds Direction East then it will print out east now we want over here to match south or north so what we can do over here is pattern matching either south or north and we can do it like that so we are here matching so we are here basically saying if this variable holds either a variant of south or north then it will print out south or north now in case it's not any of these variants then it must be West right so we can print out here West let's see and as you can see the variable holds South so this will get matched so this will get printed out match is an expression so we can use it in assignments over here we have a Boolean value fill the blank with a match expression Boolean true binary one Boolean false binary zero so what we want to do is we want to match this variable over here and then we provided the cases so if it is true then we should return one otherwise we return zero so that means the return value over here will then be assigned to this binary variable let's store that as u8 and that means binary should then hold one right because we are matching here true and then the return value should be 1. using match to get the data and enum variant holds again we have the message enum we have seen that before then over here we have an array of message variants over here we are iterating over this array and passing it to this function as you can see the argument should be of type message so we are then matching this provided argument so we want to match over here message move now as you can see the message move has struck like syntax so we have to match it like that and and as you can see we want the variables A and B so let's do that as you can see it holds Fields X and Y and that means we will then destructure these values into the variables into the variables A and B so a should hold 1 and B should hold three then we match over here the message change color variant as you can see this is a tuple-like variant and this Tuple has three elements so we can provide it some variables and we use here RGB red green blue and this is for color representation so in that case G the second value should be 255. and the last value B should be in this case 0. and again because we have to match all the cases if there is anything else that doesn't match here we will print out no data in this parents like that matches matches looks like match but can do something different so the macro matches allows us to test for example this array if a pattern matches so as you can see we are iterating over the elements of this array so over here we have the assert macro and all this does is it asserts that the given argument evaluates to true so we can over here use team matches macro and we provide it a b meaning the character we are iterating over and then we can give it a pattern it should match so as we are iterating over each element over here it should match each of these characters and we can do that like this so we want to match characters from a to and including z or a to and including that lowercase or zero two nine and notice these are characters and not integers because we have here an array of characters let's see and this succeeds as you can see all we did is we iterated over each element and we checked over here for each element if it matches if it matches this pattern and in this case everything matched because assert didn't Panic right because if one of them would evaluate to false insert will cause the program to panic let's see exercise five so over here we have an enum with two variants full and Bar then here we have a count variable which is initialized with zero and over here we have a vector now we didn't cover vectors yet but they are very similar to arrays but their size can shrink and grow meaning during a vector is dynamic in its size so as you can see we are having a vector of my enum variance two of them fool and one bar so this is a vector holding types of my inner then over here we are iterating over the elements of this vector now we want to match my enum Foo and if conditionals don't allow to match for patterns so we can't use that here but we can do something else we can hear instead using matches macro meaning we take e the element in the vector we are iterating over and we match it for this pattern right so if the element we are currently iterating over matches my enum full variant then we want to increment count by one like that and that means count after this for Loop iterating over each element should be two right because matches matched two times because there are two my enum full variants in this vector if let for some cases when matching enums match is too heavy we can use if let instead so over here as you can see we have an option of type i32 we move the whole match block using IF let instead so as you can see match is really verbal so we can use E flat instead and we do that like this we use the E flat keyword and then the pattern we want to match in this case sum and the variable should be I so the inner value here will get the structured into this variable I and then we are matching on all and if this matches then we want to print out these two lines as you can see this is much cleaner and this will get printed out this is a really long string and seven as you can see we here provide I and I just holds the inner value of this sum variant fill in a blank we have here a full enum with a bar variant holding a type of u8 then over here we have an instance of the bar variant holding a value of one now over here again we could do the same thing we can use E flat and then we match the pattern so if the pattern is full bar then we want to destructure the inner value to a variable I and we are matching on a as you can see this compiles so we have a full Anum with three variants and only the last one holds a value so over here we instantiate the full enum with a variant of cues and a value of 10. I hope I pronounce it correctly so remove the codes below using match instead so if I run this code then this would actually work because we are using IF let here but we can do it in a match statement so we match on a right and then we can provide the pattern so in case of full bar then we would then we will print out this line in case of Foo Bass we want to Output this line and in case of anything else like that we print out this line now I can delete that and as you can see much cleaner so in this case match others shadowing fix the arrows in place again we have here an option with an inner value of i32 then we are here using E flat to destructure the inner value to H and that means we are actually shadowing H because as you can see this variable here has the name H and we are then destructuring into a variable h so that means H has been shadowed create a new variable with the same name as previous age now over here as you can see H would be sum 30 which is wrong because we are actually here destructuring it right so H doesn't hold some 30 anymore but it will hold 30 because we are here destructuring it the new variable H goes out of scope here right so this variable over here goes out of scope and image can also Shadow a variable so we can here essentially doing the exactly same thing like in the uh like in the E flat statement over here foreign this would be the output from this match here alright see in the next topic so let's see patterns we can use this sign to match several values and we can use this one to match an inclusive range so as you can see we have here a function match number which takes an integer as argument then we are matching this integer and if it is one then we will then we'll print out one and fill in a blank with this don't use ranges okay so we have to match here two to five so we can do it like that and this pattern is basically saying if n is either 2 or 3 or 4 or 5 then we will print this match an inclusive range so if the so if n is between 6 and 10 where 10 is included then it will print out that and for any other case because again because again in a match statement we have to match every case so in this case we will print out that the add operator lets us create a variable that holds a value at the same time we are testing that value to see whether it matches a pattern so let's see we have here the point struct that has two Fields with I 32 types fill in the blank to let P match the second arm so we have to complete here something we have here an instance of the point type now let's see the match here so we match on P and as you can see this is how we would match a struct so we provide this track name and then the fields as you can see we are matching X and Y so so if Y is equal to 0 that means this arm here matches and this will get printed out and we then can use x over because this is actually a destructuring assignment so this would be the same as writing it like that but this is shorthand syntax so we are then using X here now in the second arm as you can see we are matching X and Y Fields so X should be between 0 and 5 5 included and Y should be either 10 20 or 30. now as you can see when we are matching here this pattern we can't use x we don't use x here but we can in fact use y because we are using here the add operator so this means we are destructuring the variable y of the field y so that means the value that Y is holding over here will gets we'll get this structured into the Y variable and at the same time we are testing why if it is matching this pattern and the last arm as you can see we don't match for any pattern other than this main pattern and then we can use X and Y because again this would be the same as writing it like that all right now we want to match the second arm that means we have to provide values that match these patterns so let's see X between 0 and 5 so let's do three and Y 10 20 or 30 I will do 30. as you can see this second arm matched fix the arrows over here we have the message enum that's holding which is holding one variant with a struct like syntax as you can see this is one field ID of type i32 then over here we are instantiating this variant so this will be an instance of the message enum and then over here we are matching this instance so if it is hello then we match the ID field so is it between 3 and 7 then we want to output here the ID now the problem here is that we did not actually destructure this field here into its distinct variable right so we have to provide here each variable let's do ID because that's the one defined here and for that we have to use the add operator so we are destructuring and matching the pattern at the same time and over here this is already done as you can see we can also provide any a variable with another name as you can see so the value of the ID field then we'll get this structured into this new ID variable right it doesn't have to be the same variable name as the field we have and again we are destructuring and at the same time pattern matching and that's and that is the reason we are using the add operator and as you can see when we are matching like that then this ID over here got this structured because this is the shorthand for this all right and when we have a pattern here we have to wrap it inside parentheses that's also important and as you can see in this case the first arm matched right because ID over here the value is between 3 and 7. in match card is an additional if condition specified after the pattern in a match arm that must also match along with the pattern matching for that arm to be chosen fill in the blank to make the code work split must be used alright as you can see we have here an option with an i32 value then we have here split and we are here matching the num variable and we are doing that to basically restructure the inner value so we are putting the value of 4 into X right so we can then use x over here but we over here also want a match card to check if x in this case this value over here is less than split we can do that like this foreign so this basically is just for destructuring the this value into X and over here we have an additional conditional in match card to check for a more specific condition to be true so if num is a sum so if num is of some variant then we the structure X and check it again and if all of that matches then we execute this if it is a sum but yeah but the value over here is actually bigger than split then this will get executed and if num holds a none then we just return a unit type ignoring remaining parts of the value with with this so as you can see we have here a tuple I want annotate that so we are matching on numbers now we only care about the first as you can see and the last one so what we can do over here we are matching on a tuple right so this is a tuple and we want to destructure the first and the last values into the variables first and last we can do it like that right so we have the first then anything in between and then the last so first should all two and last should hold 2048. using pattern mutable reference to V to match a mutable reference needs you to be very careful due to B being a value after matching let's see fix the arrow with least changing don't remove any code lines so over here we have a mutable variable holding a string and this would be a variable that holds a mutable reference to a string now we match r and that means over here we can destructure it directly into a value like that so this string over here will then be destructured into the value directly we don't have to match mute the reference like that right and that means the value variable will hold immutable reference to a string and of course then we can modify this stream over here all right that's it see you in the next one a method is a function that is associated with a particular type or struct it takes parameters and returns a value like a function but it's defined as a member of a struct or an enum it's called using dot notation like accessing members of a struct and it's implemented through an implementation block let's see an example over here we have the struct rectangle with two fields and as you can see we can then Implement methods for this rectangle type using the input keyword and the name of the type we want to implement the methods for so then over here we can see that rectangle has a method called area it takes as an argument a reference to self and self stands for the name of the instance so inside the method we are multiplying self dot with this field by self dot height this field and then we return the result of this operation now again self over here will be replaced by the name of the instance so when we create an instance of rectangle with concrete values then as you can see we can call this method on the created instance right one meaning when this method is called like that then self over here will be replaced by rect one so here rect1 dot width times rect1 dot height meaning 30 by 50 will be returned now let's see Associated functions an Associated function is a function that is associated with a struct or an enum but doesn't take an instance as its first parameter so you can recognize an Associated function if it doesn't take self as its first parameter it's called using the name of the type not an instance of it and it's often used as Constructors for a struct or an enum so let's see an Associated function as you can see we have again the rectangle type and we Implement over here again in an implementation block the new Associated function for the rectangle type and notice we don't have a self here and you will see in a minute why that is now the new Associated function takes two arguments with and height both of type u32 and it will then create an instance rectangle and returns it meaning when we want to call this Associated function we don't call it on an instance right because we don't have self here we call it directly on the type so in this case we take rectangle double colon and then the function new and we provide it two values 5 and 10 right that means Rec 1 will then hold an instance of the type rectangle okay let's do the exercises we will skip this example because I've showed you that before and we will do the exercises directly methods are similar to functions declare with FN have parameters and a return value unlike functions methods are defined within the context of a struct or an enum or a trade object and the first parameter is always self which represents the instance of destruct the method is being called upon so as you can see we have the rectangle struct again and over here we have the implementation block we can implement the methods for this type now as you can see we instantiate rectangle over here with concrete values and then assign it to right one select one holds an instance of type rectangle then over here we call this then over here we call the method area on rect one and we should get back 1500 so let's implement the area method so the first thing is we always start with cell so every method takes as first argument self okay then the return type will be Au 32 right because we are here doing operation with these two fields now what we want to do to get the area of the rectangle is multiplying the width by the height so we use self.width times self dot height and I omit here the semicolon so this will get returned let's see so when we so when we call the area method on the rect one instance then rect one dot width times rect1 dot height will result will result in 1500 meaning 30 times 50. self will take the ownership of current struct instance however reference to self will only borrow a reference from the instance Let's see we have again destruct traffic light with one field now over here there is the implementation block we can Implement methods for destruct Now using self to fill in the blank so we have been using self like this right meaning we take a reference to the instance now this is actually syntactic sugar for this and as you can see itself with an uppercase s refers to the type we are implementing the method on right so in this implementation block when we use self uppercase it refers to traffic light and you can use this syntax or you can use like we did before using just self right and here we want to take immutable reference because we are here mutating the color field and again and this and this and that are exactly the same but this is not mutable so if I would do it like that then as you can see these are actually exactly the same thing but I would suggest just stick with that it's more readable and it's clear what you mean Associated functions all functions Define within an input block are called Associated functions because they are associated with the type named after the impul we can Define Associated functions that don't have self as their first parameter and thus are not methods because they don't need an instance of the type to work with so as you can see we again use the traffic like struct and here we have the imple block so let's see first Implement an Associated function new it will return a traffic light contains color red must use self don't use traffic light in FN signatures or body let's first see in main what's going on so we call here the new Associated function and disassociated function is associated with the traffic light type and again notice the difference is we call this directly on the type and not on an instance otherwise it would be a method so we call new and then we want to have an instance of traffic light back write this new function should return an instance of traffic light meaning we can then call upon this instance light the get State method and return Red so let's see over here we are expecting no argument right so let's leave it like that and remember Associated functions don't take self now the return type would be traffic light instance and because we should use self in here we can do that and again self in an implementation block refers to the type in this case traffic light then in there we want to create an instance of traffic light so again we can use self instead of this name and then we like normally instantiating it providing it concrete values so in this case we want this self.color field to hold a string of red like that and as you can see then we should get and as you can see when we call this Associated function we'll get back an instance with this concrete value a string red then we call upon this instance light the get State method so again self here refers to light because we are calling the method on this instance and it takes just a reference to the instance meaning light is usable even after this method call and all it does it will return the value inside the color field of the instance in this case slide so light dot color will hold a string of red multiple input blocks each struct is allowed to have multiple input blocks again rectangle struct and using multiple input blocks to rewrite the code below so we can either put all of the functions and methods in the same input block or we can even reorganize it using various different input blocks so we can for example put that in its own implementation block like that and it will work exactly the same this is just for organizing and restructuring enums we can also Implement methods for enums so over here we have the enum traffic light color with three variants Implement traffic light color with a method so over here in main we are instantiating traffic light color with a variant of yellow this would be the type annotation and then when we call a method color on the C instance it should return yellow so let's see so we could Implement that using a match statement then we match a self and if match self would be this then we return the string yellow okay and we can over here instead of writing the whole name of the type we can again use uppercase self referring to the type this method is implemented on in this case traffic light color and remember in a match statement we have to handle all the possible cases meaning we have to create an arm for right and for green two so let's do that let's see and as you can see because we have here an instance which holds the variant yellow we will get back yellow when we call the color method all right that's it until next time until now we have only been dealing with concrete types but we can also deal with generics and generics are placeholders for concrete types it enables writing more reusable and flexible code and it avoids having duplicate code for different types it's a zero cost abstraction meaning the rust compiler will add compile time fill out the generics with concrete types and there is also a concept called const generics I will just briefly touch upon it because this is more of an advanced topic but I will show you one exercise just so you get the idea from it so const generic is a type parameter that represents a compile time constant value it allows to write generic code that operates on values that are known at compiled time and it's mainly used for array sizes bitwits and other constants and you will see what I mean when you see the exercise let's start with generics so as you can see over here we have a struct a now this would be considered a concrete type because we don't deal with generic types in here now destruct s takes as an argument an instance of destruct a this is also considered a concrete type because again there are not generics used here now destruct s-gen is considered a generic type because we are dealing here with a generic type parameter and this would be denotation when you declare a type for example then after the name of the type you annotate a generic type parameter like that using these symbols over here and then providing a name and by convention this is usually T standing for type and that means this struct can hold any type now as you can see we have here some functions this function over here takes s which is a concrete type the Gen spec takes as an argument an instance of s gen with a type of a now again a would be a concrete type meaning the type parameter over here is specified to be a concrete type so we can only pass to this S10 struct an instance of destruct a then over here we have the genspec i32 function which takes as an argument the S-10 struct over here with a value that is of type i32 and over here we have a generic function meaning we are declaring after the function name the the generic type parameter T and then we can use it on here meaning that we can provide as an argument over here an instance of S-10 with any type so let's see using the non-generic functions so these over here are all non-generic as you can see because we didn't Define a generic type parameter and all the values it is using are concrete right over here we have S over here we have S10 and this is taking as a value T over here the concrete Type A and over here it takes as concrete type i32 so all these are non-generic functions the only generic function over here would be this one so let's see so we want to call Direct F and function and as you can see in the function signature we are taking as an argument a type of s so let's provide that now we have to pass into s the concrete type a right because that is defined in this struct then gen spec t so this function over here takes an S10 struct with a type of a now again a would be a concrete type so we provide here S10 and a right so we are passing to the S gen struct over here a value of type A and in this case we want it to be S10 and let's say 7 because over here we Define that this function should take as an argument a type S10 which holds an i32 integer then explicitly specify type parameter chart to generic so over here we are calling the generic function meaning we can pass to it and S10 type holding any possible type now we can explicitly specify a type parameter for this T over here and we are here saying T should be a char meaning we are calling the generic function over here with the type S gen and passing as a value a type of char let's say a and implicitly specify type parameter chart to generic so we don't have to annotate the type here it will be automatically inferred by the compiler so if I do the same thing over here let's go with Z this time as you can see this is compiling now again because sgen is defined to be generic over its type pass any value so let's pass for example a floating point as you can see this is also compiling because T over here is a placeholder that stands for any type possible but again when we annotate the specific type then we can only pass a character in this example so if I try 7.7 here this would be in compiler error if I Define f64 as you can see then it's working all right a function call with explicitly specified type parameter looks like that and this is also called the turbo fish syntax so let's see implement the generic function below so as you can see we are calling here the sum function with different types and that's the whole point of generics so here we are calling sum with two I8 integers over here we call this sum function with 2i32 integers remember if nothing is annotated then the default integer type is i32 and here the same thing but for floating Point f 64. so we have to use here generics otherwise this won't be possible over here we are creating our generic type parameter and again by convention this is called t then we take here the arguments so let's say a should be of type T and B should also be of type T and then we return a type of t meaning if we pass here to i8 integers then T will get converted to an I8 right this is just a placeholder and because we operate then on two i8s the type the return type will also be I8 so what we do is just adding together the two arguments and return it and over here we have to implement a trade bound and we'll cover that in the next section so we have to Define here that t should implement the add trait otherwise we can't use this plus sign over here and again we will cover that in the next section when we look at trades and as you can see this is compiling so we can pass to sum two numbers of any type let's see number three Implement struct point to make it work as you can see we are instantiating two point structs but over here we are passing to the fields different types so in the first instance we would have i32 values in the second instance we are dealing with floats so to make that possible we have to use generic so let's implement the point struct and we are defining here the generic type parameter and then we can Define the fields and as you can see the fields hold the type t meaning if we pass it intervals then X and Y Must both be integers if we are passing it floats then both of these fields must be of type floats now because we are here defining a generic type parameter this parameter over here becomes part of the type annotation so over here we are providing to the fields of the point struct values of type i32 so we must annotate that and over here we are passing f64 and as you can see this is compiling now again we don't have to do the type annotations actually I just want to make that clear as you can see the compiler is able to infer that for us modify the struct to make the code work don't modify this code so as you can see we are here instantiating a point but the fields over here hold different types so this is not working because T over here defines that both of these placeholders will be filled with the same concrete type so in this case if we are passing X over here with an i32 type then the compiler thinks that y should also hold an i32 now what we can do then is defining another generic type parameter and by convention you just go one letter further so T and U meaning we can then use here U right and this indicates that the fields over here hold different types or could potentially hold different types they can also hold the same types and then when we annotate that we have said that the generic type parameter becomes part of the type annotation so we would pass here for x so we would pass here for T and i32 the value the type the X field is holding and over here a string so this would be the type annotation for this returned instance number five at generic for well to make the code work don't modify the code in Main so here we have the valve struct holding one field and we have an implementation block implementing one method for the well type now how do I know it's a method because it has as first parameter this self keyword and all this Value method does is just returning a reference to the Bell field value all right so when we instantiate the Bell struct over here we are providing a type of f64. but over here we are instantiating the valve field with a string so again we have different types so let's annotate here the generic type parameter then we can use T here meaning the Field Val can hold any type so let's annotate here now over here as you can see we are returning the value of the Val field and we Define here that it should be of type f64. now this would cause an error because when we call over here the Value method on the instance of Y then self.wall would hold a string right so it doesn't return an f64. now we have to use a generic type parameter also in this implementation block and we can do that like that so we have over here the generic type parameter for our type and as we've said this becomes part of the type annotation and over here we are also defining a generic type parameter T so we are then able to use it inside the implementation block so over here we can Define that it should be a reference to T right a reference to any type that the wall field holds so in the first call of value it would be an F 64 right that is the type of The Returned value that Val field is holding and in the second call over here it would be a string right so T then would be string number six we have here a point struct and we are here defining that the fields could potentially hold different types but they could also be the same and here we have the implementation block for our point struct now as you can see over here we are instantiating a point with I 32 values right so they can be the same even if we here have two generic type parameters it doesn't matter they can be the same but over here we are instantiating point with two different types right we are instantiating point with a string literal this here and HR that over here so they are different in its types then we call over here the mix up method on P1 and provided an argument of P2 and then over here as you can see we want then P3 to look something like that so we want the newly created instance here P3 to hold the value of the first instance in this case P1 as you can see we are taking X from P1 and we want the Y field to hold the value of the P2 instance of the P2 instance this character right so how would we do that with generic type parameters now let's first Implement here the mix up function and again the convention is just the more generic type parameters you are declaring you just go further in the alphabet so T U V W all right and as you can see as a first argument we are taking P1 now let's take ownership we don't care because we don't use P1 after this point meaning we can take ownership and we don't have to use a reference to self here and as an argument here we want P2 meaning a point with generic type parameters we are declaring here and the return type will be a point with a generic type parameter t and a generic type parameter of w and you will see why that is so then what we want to do is creating here a new instance and we provide here x which should be of self because self here refers to P1 right so we want self and we want the x value and Y should take it from the second argument the P1 over here and we want the Y field so let me actually annotate that as order right we take here another Point instance and this would refer to P2 because we are passing P2 over here to this mix up method so we take from P to d y field and then we are returning this instance meaning P3 will then look something like that right so let me annotate the type we would have a point with the x value of P1 and i32 and a character type of P2 the Y field right and that is what we will get let's first see if this even compiles and this is compiling now as you can see we have here a lot of generic type parameters so when we take a look at the mix-up method as you can see we are here declaring new generic type parameters because we want to Define over here that the types self and self again refers to the instance over here P1 that these types are different from the types provided on the second argument the instance of P2 right if we compare it together then P1 instance holds different types than the P2 instant so we need new generic type parameters because t and u are for this P1 instance and the other the argument we are here providing P2 will hold different types right and as you can see we are then taking from P1 the X field and from P2 we take the Y field and this would be the return type a point with t this is from P1 X and W this would be the second type from DP to point all right fix the errors to make the code work as you can see we have here is tracked point which has two fields which are generic over its type and we Implement over here a method for the point struct over here now this method over here is in fact only implemented for a struct point that holds F 32 types of fields right so if we would pass floats then this method is not implemented for a point so this is only implemented for point with a concrete type of F32 now then over here we are calling on the fields X the power integer method and let's see that in the rust documentation so as you can see the power integer method over here is actually implemented for f 64 types right so self in this case must be of type f 64. as you can see over here we have X which holds an F 64 type so we can we can call this method on X right again this is a method and not a function so the problem over here is that self in this case would be F32 which is not the right type so let's change that f64 and that means self over here would be of type f64 the concrete type we are providing here and then of course the return type would be f64 right because we are here performing an operation on two f64 types now over here of course we have to provide floating Point values right because again this method is only implemented for a point that holds as Fields F 64 types like that all right and then we can use this method as defined in the standard Library and of course over here we just have to annotate one type right because we only have one generic type parameter and as you can see this is compiling and I will see you next time so I've told you that we will take a brief look at const generics and I will just solve one exercise and leave it at that because constant generics is a more advanced topic and you don't need it that much in your everyday programming so they're useful at some situations but they are not that much used so let's see an example over here we have here a struct array so we have here destruct array which defines two generic type parameters t and n now T is a generic type parameter and n over here is a const generic type parameter and as you can see each array struct holds a data field with an array of type t and n elements all right so when we look over here we have an array and I will now annotate this type of type array right this struct distract we have defined over here we are holding three instances as you can see one two three three instances of this array struct right like that now I've told you that when we have generic type parameters it becomes part of its annotations we have to annotate it over here too in fact we don't have to do that because it's usually inferred by the compiler but I will do it right now so this over here cares about the types in the aerated data field holds so as you can see we have here integers we have here floats and over here we again have integers and what that means is that in this array over here these actually should all hold the same type because we have to provide here a concrete type right for T and that means this data field over here will then hold in its array this type so in this case we will do i32 and we need to provide it a constant meaning for the length of the array in this case we Define three all right and what that means is that the arrays inside the data field of these instances must be of i32 type and they must have a length of 3. right so we can't use floats here we have to use i32 and over here we need to add another element now they can't be different for example let's do it like that that is possible but all of them over here must have the same type and the same amount of elements because we are annotating that here so it complies with the generic type parameter annotations here now let's create another array with floats now I want this to hold array instances of type f64 and let's do two elements and I will pass here let's say also three and that means inside here let's first look at the outside array at this array over here so we want three elements of type array this array struct right so let's define that like that as you can see we have here three elements of type array now we Define here for the generic type parameters that the data field the data field should hold an array right because this is defined over here in the array struct so each data field should hold an array of f64 right the T over here of two elements right this is the const generic parameter so we are passing here a constant and not a type so in this case we the data field would have concrete types of f64 and two elements right so let's fill that out like that as you can see this is compiling because we are here complying with the generic type parameters we have three array instances right and each array instance holds a data field with these types f64 and they have to be two elements inside the array all right see in the next topic we will now cover trades so a trade is a set of methods that can be implemented for multiple types in order to provide common functionality and behavior between them a trade consists only of method signatures which then have to be implemented by the Target type it's similar to classes in other languages but not quite the same and it defines shared behavior in an abstract way so let's see how that might look like as you can see over here we have two custom types sheep and Cal now we want to indicate that these are somewhat related so over here we have created a trade anime which defines one method then over here we are implementing the animal trade for sheep and for cow now when we Implement a trade for some type then we have to implement all the methods the trade defines so in this case as you can see in the trade animal we only provide it with a function signature and then the concrete types for example sheep and cow will implement the method and Its Behavior so as you can see both sheep and cow then implement the animal trait meaning they automatically implement the method right so both of them take a reference to its instance for example an a ship instance and then just return the string over here and the same thing for cow now there is also derivable traits and we have seen that before a trait can be automatically implemented for a struct or an enum by the rust compiler these are called derivable because they can beat derived automatically and the most common derivable traits are debug allowing to Output content via this debug notation we have seen that clone which enables a type to be duplicated with the Clone method copy enables a type to be copied implicitly without requiring explicit clone method and partially queue enables comparison now we can pass trades as parameters so the notify function over here takes as an argument any type that implements the summary trade meaning we can ensure that the item argument over here implements this summarize method right if we provide here A type that didn't implement this summary trait then it wouldn't have this summarize method trades can be used as parameters for functions the function notify takes as an argument any type that has implemented the summary trait now there is also a concept called trade bounce meaning if we declare over here a generic type parameter we can define that the type over here must implement the summary trade so the argument we pass to this notify function must implement this summary trait and this is similar to the example using the impulse summary but more verbose so as you can see this would actually be exactly the same like we did over here trade bounds are declared like generics after the name of the function and use trade bounds if you have lots of parameters to avoid this so if we would have here an argument which takes a typed Implement summary and another argument which again takes a type that implements summary we can do it like that so we Define here a trade bound meaning the generic type T must implement this summary trait so both argument 1 and argument 2 take a reference to a type that implements the summary trait and if you have big trade bounds like for example this we have here generic type parameter T and we say here that the type T has to implement the display and decline trades and the generic Type U has to implement the Clone anti-debug trait right and then over here as you can see the first argument must be of type T and the second one of Type U and we are taking references so we can write that in a more concise way using a verb Clause meaning after the return type in a function we can use the verb keyword and then over here Define our trade bounds and this over here is exactly the same as this but instead of defining the trade bounds directly on the generic type parameter we do it in a separate block foreign and we can also return from a function a type that implements a specific trait so as you can see the return dog function over here returns an instance of the dog struct and because the animal trade over here has been implemented for torque we can also Define the return type as follows we return here an instance of talk meaning we return here a type that implements the animal trade because as you can see animal is implemented for the dog struct let's see exercise one feeling the two input blocks to make the chord work don't modify the code in main now we have here a trade hello finding two methods now over here you can see that we didn't only provide the function signature but we implemented basically the whole method why is that because this is considered the default implementation meaning any type that implements the hello trade automatically gets this default implementation of this method so we don't have then to implement it manually it will automatically get implemented for the type that implements the hello trade but as you can see over here we have a method which is here just a function signature so we have to implement that manually then over here we have two structs student and teacher and we Implement over here the hello trade for student and for teacher so let's see we have here an instance of student and as you can see this is just a struct that doesn't hold any values right so we instantiate it like that but because we have here implemented hello for the student struct that means we can then call upon it this method over here as you can see we call this say hi method on the S instance and then it should return high now we don't have to implement this say hi method over here again like that we don't have to do that because it's already implemented by default right but what we have to implement is to say something method because as you can see we are calling the say something method on the instance of s and remember when we Implement a trade for a type in this case student we have to implement all the methods except the default implementation here so let's actually take this and copy it over because the function signature must be exactly the same like defined in the trade and then over here we want the return to be I'm a good student so let's return this string here like that so when we call on an instance of student this say something method then it will return I'm a good student now over here we are instantiating the teacher struct and as you can see we are calling the method on the T instance and this should be the returned string so as you can see this is different from what is implemented in the default implementation and that means we have to re-implement this say hi method for the teacher type and the return string should be hi I'm your new teacher so as you can see if a trade defines a default implementation we can also overwrite the default implementation and provide another return value and of course because we have to implement this over here I will copy that and I will implement it for the teacher type so as you can see when we call the say something method on an instance of teacher then we should get back this string so as you can see we have then implemented the hello trade for our custom types student and teacher let's see if that compiles and this is compiling derived the compiler is capable of providing basic implementations for some trades via ddrive attribute for more info please visit here let's see over here we have a tuple struct centimeters which holds a value of type f64 now as you can see this truck here derives to trades partially q and partial order meaning the compiler will automatically Implement these trades for our type centimeters now partially Q is for comparing equality and partial order is for ordering so over here we have inches and and inches is a tuple struct that holds an i32 type and it derives the debug trade meaning we can print it out using the debug notation and as you can see over here we Implement a method two centimeters for an instance of the inches type so when we call this method on an instance of inches then we will convert it to centimeters right so we hear the reference self meaning the instance of inches and then we destructure over here the inches basically the value that the Tuple struct inches holes so this variable then will hold the value of the instance provided here okay and then as you can see we are multiplying inches by 2.54 to convert it to centimeters and we are wrapping that and we are returning that as an instance of centimeters as you can see add some attributes to make the code work don't modify other code so as you can see over here we are instantiating this seconds struct so then we are printing it out using debug notation so we have to derive over here the debug trade then we are here comparing for equality so we have to derive The partially Q trait and over here we are checking if one second is bigger than one second meaning we need to implement the partial order trade like that and as you can see we are here instantiating the inches struct providing it a value of 12 right this i32 type and then we print it out and let's check debug is implemented so we can print it using debug notation and over here we are instantiating the centimeter struct as you can see centimeters takes a value of f64. and over here we are comparing as you can see over here this foot instance holding an instance of inches with 12. so in the U.S 12 inches equal one foot and then we call the two centimeters method on it mean meaning this will then return an instance of centimeters right so we are taking here inches 12. and that means 12 over here will get destructured right here we have here self would be inches 12 and we take it as an reference like that so we destructure it like that using the let keyword the reference because we want to take the value and not a reference and then 12 will be put inside the inches variable then we are multiplying by 2.54 and returning an instance of centimeters and that means we can then compare this over here with the meter because the meter holds also the same type namely the custom type centimeters over here right so both types would be here of type centimeters and if this is smaller than this then it will assigned to this variable a string smaller otherwise bigger and these are string literals by the way and over here we are outputting the result of this compare let's see and as you can see this is compiling so we are outputting here one second looks like seconds one as you can see we can output this type over here seconds because we have derived the debug trade meaning we can use debug notation then one foot equals inches 12. and over here this one foot is smaller than one meter operator in Rust many of The Operators can be overloaded via trades that is some operators can be used to accomplish different tasks based on their input arguments this is possible because operators are syntactic sugar for method called example the plus operator in a plus b calls the add method as in a at B over here this would be a method this add method is part of the add trade hence the plus operator can be used by any implementer of the add trade so Implement implement the function multiply to make the code work as mentioned above plus needs T to implement standard Ops add trait that's why we are here importing the Ops module from standard Library so let's implement the multiply function as you can see over here we are providing two u8 integers to the multiply function now in the second call over here we are providing flow means we need a generic type parameter like that so this function takes two arguments and it will return T basically the same type of the provided arguments and then over here we basically just want to multiply a by B now as you can see over here we are using the operator for multiplications and that means we have to ensure that the T over here the type of the arguments that the multiply function takes implements the Mal trait now the mall trade like the add trades over here then knows that if both of the types of A and B Implement The Malt rate it is possible to multiply these two together so we can use here a trade bound meaning the type T over here should implement should implement the standard of malt rate because again this is just overloading and syntactic sugar for this call right so this actually would translate to this and we are seen in the add over here that it would be a at B right these operators are just overloaded so it's easier to use and that's why we have to ensure that a this argument here of type T has actually implemented the Mal method and we make sure that this is the case when providing here a trade bound meaning T must Implement The Malt rate meaning then that t has implemented the Mal method let's see and we have here to also find that the output will be of type t because as you can see the mult rate over here has an Associated type of output so we have to Define and we will take a look at Associated types but we have here to Define that the output should of type t that the output should be of type T right let's see again and this is working fix the arrows don't modify the code in main so over here again we are using the Ops module from the standard Library then we have here two structs full and power and full bar and Powerful the standard Ops add trade is used to specify the functionality of the plus operator here we make add bar to trace for addition with a right hand side of type bar the following block implements the operation full plus bar should return powerful so if we USD plus operator with these two types over here we should return this type right full plus bar should return full bar now let's see the add tray now the add trade over here takes as default parameter rhs standing for right hand side if we don't provide anything to right hand side then it will be self meaning the type we implement the add trade on and as you can see we have an Associated type here for the output and over here as you can see it takes self and basically becomes the owner when called on an instance that implements the add trade and right hand side is off type right hand side which means by default it would be self and this looks confusing I know but you will get it when we Implement that over here so as you can see we are implementing from the Ops module the add trade so we can Implement from this standard Library trades for our custom types and let's see again when doing something like that then then the compiler will actually do something like that right it will call the add method now this over here is the type here would be an instance of Foo but just so you get the concept and as you can see we have here to define the right hand side meaning the right hand side over here would be poor and we are implementing the add trade for Foo right because we want the instance to be of Type 4 so we implement it for Foo and we want the right hand side to be of type bar now if we wanted to add for example then we could omit that because you are seeing that the default itself right so the default right hand side would be self as you can see so we can omit that but the bow but that means both types must be the same in this case Foo and fool right because self over here refers to the type we are implement the trade four right but in this case it would be bar right because the right hand side is different from the left hand side and the output should be of type full bar so as and again Associated functions we will cover that very soon so over here we have to implement the add method right because the add trade over here defines an add method so we have to implement it and that means we take self and this is taking ownership of an instance of Foo right and right hand side would be of type bar again as you can see Foo would be the instance then we call the add method upon the instance and this argument should be of type par and then we return full bar so in Main when we call so in main when we perform this over here full plus bar we will get back full bar right so we have implemented the plus operator on our custom types now let's do the same for minus sign but in this case full minus bar would be powerful so let's see as you can see this would be distract we want to return powerful so minus sign is actually the same but implementing this sub trade so we will type full sub R right this would be the sub method defined in this sub trade as you can see we are here implementing the substrate from the Ops module for bar now again we want actually to implement it for Foo because here we call Foo support meaning we would implement it for the foo instance because cell 4 here refers to the instance the method is called upon right so the sub will be called upon self meaning we have to implement it for Foo and the right hand side would be bar now we want to return bar food that's true and over here as you can see the sub method is implemented but as right hand side the type should be bar right this is the right hand side of the operation and we are returning powerful so when we do this full minus 4 we will get back powerful so we have overloaded this operator and of course to do assert EQ here because this is a custom type we have to implement the partial EQ trade so let's do that and we also have to implement the debug trade as you can see this is compiling meaning when we use our custom types with these operators they will return a custom type and we can do that by implementing from the Ops module from the standard Library the appropriate trades for our custom types use trade as function parameters instead of a concrete type for the item parameter we specify the input keyword and the trade name this parameter accepts any type that implements the specified trade as you can see we have here a trade summary Implement function summary to make the code work fix the errors without removing any code line so over here we have a struct post with three fields of type string and we Implement over here this summary trade for the Post struct meaning we have to implement the defined method in the trade so we implement it for post like this as you can see we are accessing from the instance remember this is a method so it refers to an instance it is called upon right so we access the fields self.title and self dot author all right then over here we have a struct Weeble with two fields of type string then we implement the summary trait for Weeble and we then have to implement the method from the trade all right because again when we Implement a trade for a type we have to implement its methods so this over here accesses the username and contact field and prints it out now here we have an instant of post and here we have an instance of weibo so as you can see we are here calling this summary function with an argument of post and weibo so we have to implement this summary function below let's do that now as an argument it takes now as an argument it takes in the first call Post and in the second call Weeble right two different types but now think about it what do they share in common write they implement this summary trade right we have implemented it for post and for Weeble so we can Define over here that the argument provided must be a type that implements the summary trait and we do it like that and then inside the function body we want to take this argument and again a is an instance of a type that implements the summary trait so we are taking a and we call upon it the summarize method meaning the first one it will be post dot summarize now post over here is of type post meaning we go to post and as you can see this would be the summarize method as you can see self would refer to the instance in this case post this instance over here so then the summarize method will return a string so let's actually assign this to a variable this would then hold this string and we can then output like that let's see and of course when we are calling the summary method with an instance then we can't use the instance again so let's define here that it should take a reference right and as you can see this is compiling now what we can do over here and this would be exactly the same we can Define here a generic type parameter T and here Define that it should Implement summary right so we Define here the type t and it should be a type that must Implement summary and then we can write it like that so a should then be a reference of type T which implements summary trait let's see as you can see exactly the same thing returning types that Implement rates we can also use the infiltrate syntax in the return position to return a value of some type that implements a trade however you can only use input trade if you're returning a single type use trade objects instead when you really need to return several types so over here we have two custom types sheep and Cal and we have one trade animal which has one method so over here we are then implementing the animal trades for both of these custom types and as you can see when we Implement a trade for a concrete type then we have to implement the given method so over here we have a function random animal it takes a random number and then and then looks if the random number is less than 0.5 it will return a sheep instance otherwise it will return a cow instance now over here the return type of the function would be a type that implements the animal trade which both of them do and as you can see in main we have here our random number and we call this random number on the random animal function so let's for now assume that this really is random okay so that means that when this is passed over here it could either be a sheep or a cow to be returned here but the problem here is that the return type in a function must be of known fixed size at compile time which in this case sheep might be bigger than cow or cow might be bigger than sheep so the exact size is not known at compile time so when we do that this doesn't work and to make that work we have to know about trade objects so let's see so trade objects using input traits doesn't work when returning multiple types different implementations of a trade probably with different amounts of memory but sizes of types must be known at compiled time in this case a trade object can be used a trait object is essentially a pointer to any type that implements the given trade where the precise type can only be known at runtime so over here for example we have a trade animal and two custom types token cat then we implement the trade animal for both of these types and as you can see we have a function return animal it takes a string and if this string is dark then it returns a dog instance now if it returns cat then it returns a cat instant but as you can see we are here providing a reference to the instance and not the incense itself because when we return a reference to a type that implements the animal trade it means that the size of this type is known at compile time because remember a reference is a pointer and the size of a power and the size of a pointer is known at compile time meaning it's of Type U size and U size is 8 bytes on a 64-bit computer so the size of the return type is known it's just a pointer sized type so the size of the return type is known at compile time it's of size U size right because we are here returning a pointer meaning it doesn't matter if dog is bigger than cat or otherwise it's always the same size because pointers have a fixed size at compile time and that is how we would solve this problem so again here we have a function which so here we have a function which returns a type that implements the animal trade this could be dog or cat as the trade object is behind the pointer the size is known at compile time which is U size the size of a pointer this allows for more flexible code as the exact return type doesn't have to be known at compile time as long as the size is fixed so we don't care anymore about the size of dog and cat because they're behind a pointer and to understand what's going on you have to know about static and dynamic dispatch so let's first look at static dispatch static dispatch resolves method calls at compile time the compiler will generate function code for each concrete type that implements the trade it calls the appropriate function based on concrete types it's faster and more efficient than Dynamic dispatch but it doesn't provide great flexibility so we have here the trade animal holding one method say hi then we have Talking Cat and we implement the animal trade for dog and for cat now as you can see when we create an instance from dog and from cat and call the say hi method on it because as you can see every every type that implements the animal trait must have this method and when we then run this program then the compiler will actually generate methods for each concrete type in this case stock or cat because then the say hi method we are calling here on the dog and cat instance is known at compile time so the compiler then knows when we call the method on the dog or cat instance which method has to be resolved for which type so let's see Dynamic dispatch in Dynamic dispatch the specific methods to be called is determined at runtime and not at compile time like static dispatch it works by creating a reference or smart pointer to a trade object using reference din or boxed in when trade object is created the compiler will build a v-table for that trade a v table is a table that contains a pointer to the implementation of each method in the trades for the specific type of the object that the reference points to the compiler will do a lookup in a V table to determine which method should be called for which type that implements the given trade and this lookup will cause overhead but allows for more flexible code so exactly because of this lookup static dispatch is faster than Dynamic dispatch but on the other hand Dynamic dispatch allows for more flexibility so let's see how that might look like as you remember with box we are able to allocate a type to Heap memory meaning the type will then get allocated for example a i32 integer like 7 will get allocated to the Heap and we will then get back a pointer this is what box is for and we have seen examples of that now when we use box with a trait object it means that the type that implements the specific trade will get allocated in Heap memory get back a pointer to the location where the type that implements this specific trait was allocated and we will get back a v pointer right this is happening when dynamically dispatching so the V pointer points to the V table and in the v table there are certain Fields now there is also a field called Methods and this is where all the methods for the concrete type over here are located meaning at runtime the compiler will check this V table and see in the methods over here which methods are implemented for the concrete type that was allocated in Heap memory so let's see an example so we have over here a trade animal and we have two custom types cat and talk and we implement this method defined in the trade for both of our types right because when we Implement a trade for a custom type we have to implement all of its defined methods so over here we have seen we have a random animal function which takes a random number and based on this conditional it will either output a cad instance or a dog instance so then when we call this random animal function with the random number and for now just assume that this is really a random number that means the compiler first of all cannot determine the size of the type which gets returned and this is of type that implements the animal trade right cat and dog both implement the trade but we can't know the exact size at compile time meaning we put it behind a pointer in this case a box but a reference would also be possible and that means the size is now known right it's the size of a pointer basically U size 8 bytes in a 64-bit computer but we have another problem over here the return value will be a type that implements the animal trade but at compile time when we call the noise method over here the compiler doesn't know which of these types get returned so he so the compiler can't know which method he has to call is it for the implementation on cat or dog right so this is why we use Dynamic dispatch because that means this over here will get decided at run time so again the compiler will then look up in this B table over here using the V pointer which points to the V Table stores in heaped memory and and then it will check for the concrete type that got allocated on Heap memory this specific methods so for example again this is happening at runtime we return a cat then the compiler will see that the type over here which is concrete is cat so it will see in the v table the methods implemented for the concrete type of cat I hope this makes sense so this is why we need Dynamic dispatch here static dispatch doesn't work because again the output of this function is random so let's see box box is a smart pointer that allows to store data on the Heap rather than this stack and you can use box when you have a type whose size can't be known at compile time it returns a pointer to the data stored on the Heap so what's the difference between a reference and a box in terms of memory a box allocates the data on the Heap and owns it is also responsible for deallocating when the value goes out of scope and the reference only points to a value that is already in memory meaning the reference points to something that is already there while a box allocates and a and the reference also doesn't own anything it just points to something in terms of lifetimes a box can be passed across Scopes while the reference has limited lifetime and we will cover lifetimes very soon a box can be cloned and the reference cannot and the Box can also be used in pattern matching so we have left off over here and I hope by now you know that this over here is not possible we cannot return a type which implements the animal trade because these could vary in size and again over here we return a type that implements the animal trade now if this over here would be really a random number then we don't know exactly at compile time which type gets returned meaning we can't use here the noise method because the compiler can impossibly know at compile time of course which method he has to call is it this one or is it this one right and for that we will use Dynamic dispatch so again we can put this in a box and using here the pin keyword meaning we want to go with Dynamic dispatch here and that means we have to box these instances so now the return type over here is known at compile time meaning we can annotate it it would be a box which holds a type that implements the animal trade and this is using Dynamic dispatch meaning we then can call this noise method on this instance here because the compiler can check for this noise method at run time and not at compile time meaning it is determined which of these will get called at runtime when the program is running let's see and this is working trade bound the infiltrate syntax works for straightforward cases but is actually syntax sugar for a longer form which is called a trade bound when working with generic type parameters often must use trades as bounds to stipulate what functionality a type implements so over here we are calling the sum function with two arguments in this case 2i32 arguments and as you can see we could also call this with floats because we have here defined t t generic type parameter so as you notice X and Y Must Be of the same type T and the type will be T which gets returned and all we do over here is adding X Plus y now again to use this plus operator here we must Define that t over here is a type that implements the add trade because remember this would actually translate to this so it's critical that X implements the add trait right and we also have to Define over here the associated type output to be of type t let's see this is compiling let's actually call the function and print out the result so as you can see the output would be 10. and we can call it with float right so let's see exercise 8 fix the errors as you can see we have a pair struct which has two Fields holding values of type t meaning they are generic and then we have an implementation block for repair and we Define here an Associated function and this is not a method because it doesn't have self as parameter now over here we are instantiating a new pair and remember self over here uppercase s refers to the type of the implementation block meaning pair and then we return this instance so if I write pair here it would be the same thing then over here we have another implementation block for the type pair and as you can see we Define here that t over here should implement the debug and partial order so the type T over here the type of the two fields the type of the two Fields must Implement debug and partial order then over here we are defining a method compare display which takes a reference to an instance of pair and then checking over here is the value of the X field bigger than or equal to the value of the Y field if that is the case then self dot X will get printed out using debug notation that is why we need T to implement the debug trade otherwise we print out the value of the Y field so as you can see over here we have a custom type unit that holds an i32 integer now over here we are instantiating a pair struct and as you can see we are passing here as concrete values for X and Y Fields the unit type our custom type and over here it holds one and here three write an i32 the type annotation for that would be we would have a pair instance and T over here would be unit right because X and Y hold a type of unit so T over here will be unit now remember we have over here an Associated function so let's actually use that to instantiate a pair as you can see this is exactly the same thing as this but here we are using the new Associated function that got implemented on the pair type so I can delete that and again when we call on this instance the compare display method then we would have an error because as for now doesn't implement the debug and partial order trades so we have to derive them and by the way we also need the partial EQ trait and that's because we are here checking for equality all right so we knew so we need partial equality and because we have now derived these trades for this type it means unit implements debug partial order and partial EQ meaning we can over here compare for equality and ordering and we can print the field out using debug notation let's see and this is running so the largest member is y you will need two so we have here a pair with an X field of unit 1 and the Y field of unit 2. and then the compare display method will give us back you will need two right because the wife because the Y field holds a value that is bigger than the X field compare these two because this is a custom type is only possible because we have derived these trades alright see you in the next topic so before doing more exercises on trade objects I want to cover Associated types now an Associated type allows to specify a type that is associated with the trade when implementing the trade for a specific type we have to specify the concrete type it's basically a type placeholder that the trade methods can use in their signature and it's similar to generic types but they are more flexible because they allow a trade to have different Associated types for different implementing types let's see an example we have here the my trade and this has an Associated type called my type and as you can see we can then use this Associated type over here in the methods we are defining in the trade so we here for example Define that the return type should be of type my type meaning when we implement this trade over here for a concrete type like for example my struct then we have to define the concrete type as you can see when we implement the my trade for my struct we Define here that my type should be i32 and as you can see this method over here will then return an i32 type and this is defined over here and we can access the associated type using self uppercase double colon and the name of the associated type now again self here stands for the type we are implementing this Trade four so in this case my struct all right let's do some more exercises practicing trade objects as you can see we have here a trade bird that has one method then we have here two custom types duck and Swan so let's see for duck we have an implementation block and here we Define a method and this method takes a reference to the instance and prints out something and for Swan we also Implement a method now notice this has nothing to do with the trade these are methods that are defined directly on the struct right so here we Define a method fly which again takes a reference to the instance and prints out something now over here we are implementing the birth trait for duck and Swan meaning we have to implement the method defined by the trade so as you can see this Quark method we have to implement for both types then over here we want to instantiate a duck so we then can call on this stack instance the swim method so this will then get executed then over here we call the hatch a bird function with an argument of two and over here we call the hatch a bird function again with an argument of one so as you can see when we provide two we want over here duck when we provide one we want over here a swan and I see that because of the output when calling the quack method so let's implement this function we have seen that the function takes as an argument and interfer so let's call the argument species and let's define it as u8 now I will keep the return type open we will see that then so let's see when we get one let's actually match the species argument when we get back one we want to return a swan if we get here for the argument A2 we want to return a duck instance and because we have to handle all the cases I will just panic in case anything else has been passed so over here what would be the return type now think about it when calling this function over here we have to provide the return type now again the return type has to be of known fixed size at compile time now over here we don't know what we'll get actually return that compile time right especially if this argument over here would be something that is generated at runtime meaning we don't know the exact size because this one struct could be larger in size than the duck struct or otherwise so we have actually seen the solution we need to box the return type and V over here Define that it should be dynamically dispatched and that means we have to box these concrete values here and now we are good to go because also another problem would be that in a match every return type has to be of the same type now again over here the function could sometimes return a swan or sometimes a duck right depending on the argument and when we do it like that we actually return a type of box right so they have all the same return type now again because we are here using Dynamic dispatch we have to put it behind a kind of pointer and that means over here we will get a box holding a trade object and the trade object in this case is bird so that means and we have seen that the compiler will then allocate the concrete type for example over here a duck in the Heap memory and we'll create a v table with all the methods because when we call over here the Quark method on bird then at this exact point Dynamic dispatch happens because it is not known at compile time which concrete type got returned here this will be determined at runtime so at runtime the compiler will check the V table and see the right and will execute the right method call right in this case it would be for duck so the compiler will look this method up in the v table for the tag type which has been allocated in Heap memory and we got and notice something over here when we return a trade object the trade object actually only implements the methods from the trade in this case Bird right so we then can't call because we have here a trade object we can't call a method directly defined on the type so for example this swim method is not in the v table that the compiler creates I hope you see that because again we are returning a trade object meaning in the v table are only the methods from the trade so over here because we have created an instance on the concrete type we can call this three method but over here because we have a trade object we can only call methods from the trade let's see and as you can see this is compiling array with trade objects again we have the birth trait with the same method then we have duck and Swan and as you can see these are methods that are implemented directly on the concrete type then over here again implementing the birth trait for duck and so on we have seen that fill in the blank to make the code work so over here we want to have an array because we are here iterating over this Birds variable meaning we want to iterate over an array now let's first annotate we want over here an array of birds right so we want a type that implements the bird trade meaning in this case stock and Swan and this time I will use a reference instead of a box so we Define over here that we want an array holding a type that implements the birth trait and is dynamically dispatched all right and again because using Dynamic dispatch requires that we put it behind some kind of pointer either box or a reference now with this array let's say we want to have two elements one duck and one swan so let's provide here the instances and again we have to put that behind a pointer so the size is known so the size is known at compile time because remember the size of the elements in an array must be known at compile time in this case it would be you size the size of a pointer now then we want over here to iterate over these elements here and then as you can see for each element we are calling on it the Quark method now again because this over here is happening at runtime the Quark method will be determined at runtime which one to run here for stuck or for swan right let's see and this is compiling now overs and here we have the same thing bird.fly the method we have directly implemented on the type isn't available because in a trade object over here only the methods from the actual trade birth are in the v table other methods won't get into DV table and so the compiler cannot look it up at runtime referenced in and boxed in so we have here a trade draw which has one method and we implement the draw trade for u8 and f64. as you can see we can Implement our own custom trades for types in the standard Library and all this does over here is it will take a reference of the instance and and it will output the value now in the newer version of rust we don't have 2D reference self here this is happening automatically like that and for f64 the same thing now as you can see we have here an f64 and an u8 basically the types that implement this draw trade then over here we are calling the function draw with box and as you can see this function over here expects a trade object of the draw trade and this trait object should be boxed all right and then it is calling the draw method again the compiler will then look in the v table and will execute the appropriate method at runtime so let's pass the argument for the draw with box function and in this case we want to draw X so let's pass it X and of course this has to be boxed right like this now over here we want to draw Y and this time we are passing a reference meaning we over here expect a reference to a trade object that implements the draw trade all right and then we are calling the draw method again this time on y let's see and this is compiling let's see exercise four we have a trade full which has one method then we Implement Foo for u8 and string types and as you can see we can Implement our own trades on types that are defined in this standard Library so in this case u8 and string then over here in main we have a variable X holding a u8 and Y holding a string basically the types we are implementing for for right and let's see now the function called Static dispatch so we pass here X tu8 type and this should be statically dispatched so let's implement this function Implement below with generics so we need here a generic type parameter and we Define here a trade bound meaning we want T to implement the foo trade right because we want the argument to be of type t so it's important that we Define here the trade bound because we call this function only with arguments that implement the full trade and we are defining that in order to be able to call the method method on it basically this method defined here right because then we can pass types of u8 and string to this function and it can call the method because the full trade is implemented for this type any other type that doesn't implement the full trade won't be allowed to be passed to this function so this is static dispatch because the compiler at compile time will actually expand this code and create a function for the concrete types so in this case we are passing u8 to this function so the compiler will create a function like this right and then we'll call it and this is happening at compile time meaning the compiler will fill in the concrete types over here and then of course the compiler will know when we do a method call which method over here it should call right and static dispatch is usually a lot faster than Dynamic dispatch because you don't have to do a lookup it's everything in the binary itself but when you have to do a lookup you have to follow a pointer to Heap memory and then get the data from this memory location which is more expensive so let's see Dynamic dispatch in this case and we use Dynamic dispatch because sometimes it's just not possible to use static dispatch right when the size of the returned type it cannot be known at compile time then we use Dynamic dispatch and let's see how that looks like so over here we are passing a reference to Y so we are passing here a reference to a string now as an argument over here we want a reference right that's what we are passing to a trade object implementing the full trade all right and again because this is dynamic dispatch so this happens at runtime must be behind a pointer so this size is known at compile time because it pointer is always of size U size we have covered that and then we call the method on the argument right the difference is that over here this will then be determined at runtime which method over here to be called right because the compiler will then do a lookup in the v table at runtime and then determine for which type the method should be called let's see this is compiling object safe you can only make object safe trades into trade objects a trade is object safe if all the methods defined in the trade have the following properties the return type isn't self and there are no generic type parameters so let's see exercise 5 use at least two approaches to make it work don't add remove any code line so I think we will go first with static dispatch because it's easier so I will remove this box here and let's see we have a trade here defining one method now this defines that it takes a reference to a self meaning a reference to an instance of my trait and it returns self their self over here refers to the type for which the nitrate is implemented so in this case for example over here my trade is implemented for u32 meaning we implement this method over here and self over here would refer to u32 right we can write it either way and u32 and string Implement my trade all right so they both implement the F method then over here we have a function that takes a trade object now in this case you want to use static dispatch so we Define here a generic type right because we are calling it with new 32 and a string now we want to ensure that t this type implements my trade so we can be sure that it implements this F method so this is called trade bound and this makes sure that t implements my trade so we then can call the F method on the provided argument here which is of type t and that's basically it we have implemented the static dispatch for this function over here and what then will happen is when we call this function the roscompiler will take this generic function here and it will provide it with concrete types and it will provide it with concrete types so in this case u32 right we are passing it to 32. and we have we forgot to define the return type so the return type over here would be the return type of the F method now in this case it would be T type right so if we are calling it so if we are calling the F method on a new 32 then the return type will be u32 in if we call it on a string then the return type will be string so again we pass here T right because the type we are passing for example u32 will be the return type of this function u32 and that in case means that the return type of this function over here will be u32 right okay and then the same thing happens for a string so the compiler will actually put into these generic type parameters concrete types and that means when we are using static dispatch these method calls here are known at compile time so the compiler knows if you're passing a u32 type then it will call the F method from the eu32 implementation of my trade right and in case of string then the compiler will know that the F method can be found in the my trade implementation of string right so everything here is known at compile time and also the size is fixed and this is working now we want here to now use Dynamic dispatch so we solve it in two approaches so before we can even use this straight object over here we have to make the trade itself object safe and we do that when the return type isn't self and there are no generic type parameters so we cannot Define here self we have to provide it concrete types now in this case we want the F method to return a trade object meaning we return a pointer that points to the type that implements my trade all right and over here because we are taking here this function signature and implement it for each type we Implement in my trade for so as you can see the return type must implement the my trade and it has to be boxed so we have to box this over here turn over here A U 32 right which implements the my trade and over here a string which also implements my trade so as you can see we got rid of self because again at compile time the return type size has to be known that's why we are putting it behind a box now again the return type of this function would be the return type of Team method calls in this case it would be a boxed trait object right let's see if this is compiling and this is also compiling now I hope you see the difference here when we pass here a trade object basically a type in this case u32 that implements the my trade trade then this method call will be determined at runtime and that's why we have to put this behind pointers so the size remains fixed and known at compiled time and again use whenever possible static dispatch because it's faster and I guess easier to write but Dynamic dispatch has its advantages and you can use it for more flexibility in your code alright see you in the next topic so we have reached the topic of collection types and string is also a collection type meaning we are just repeating because we have covered strings but I think these exercises are good for some repetitions so string is a utf-8 encoded growable string it is the most common string type we used in Daily development it also has ownership over the string contents so let's see fill in the blanks and fix arrows don't use to string don't add remove any code line so as you can see over here we want a string type because we because we are here modifying the swing meaning it has to be growable so let's allocate this data over here on the heat so we have now a string type we can now let's see the poster method as you can see this push stir method is implemented on the string type and it takes as a type for its argument a string literal as you can see we are calling it on a string and providing it an argument of a string literal so we have to delete that because this would actually be a string right we want here a string literal like that and push is for pushing single characters so we want to push here an exclamation mark So s will then here match with this string literal and as you can see we are calling the move ownership function here meaning this function will then take ownership of s and that's not what we want because we are using here as again so what we can do is just clone it so the data passed to move ownership over here is actually cloned right it is copied on the Heap memory and that means we can still use S because s Remains the owner of this data all right then a string is stored as a vector of bytes but guaranteed to always be a valid utf-8 sequence string is Heap allocated growable and not null terminated a string slice is a slice basically a slice to u8 integers because as you can see this string type is a vector of u8 inter source that always points to a valid utf-8 sequence and can be used to view into a string just like a slice to T is a view into Vector T and we'll cover vectors after this topic so fill in the blanks we have here a string and we want to take a string slice from this string now the easiest method would be just to take a reference to s which means we take a reference to a string type and this can then be inferred by the compiler to a string slice and the second way would be to call the askster method on it this will essentially do exactly the same thing it will take the string s and we'll convert it to a string slice now over here we also want a string slice but we want to provide a specific offset we just want this data over here and that means we take a reference to s and provide it here an offset from zero we can omit the zero on here until index 0 1 2 3 4 until index four now we would provide here 5 because 5 is excluded meaning we go from index 0 to 4. and over here as you can see we want to modify the string holding then hello world because we are pushing to it this exclamation mark as you can see we want it hello world and with an exclamation mark so what we can do is taking s over here s mutable reference so this would then hold a mutual reference to a string let's see and this is compiling now notice over here we could also take ownership of s meaning slice 3 will hold a string because we are not using S after this point so slice 3 can be the owner of s but in this case we have to make the variable mutable and this is also working question how many Heap allocations are happening here so we have here a string and when we create a string type then the data that is provided is allocated in Heap memory so this would be the first allocation then over here we are taking from this string over here a reference meaning a string slice and remember a string size is just a view into some data so we are just going to this location and checking what's in there so no allocation is happening and over here we are taking this string slice and convert it to a string meaning a heap allocation is happening over here right because we are taking this string slice holding this and then we allocate this into the Heap once more being because we are calling this two string method let's see indexing so we have a string here and we want to index into the first element so slice one should then hold h so we want a string slice so we take a reference to S and provided the offset from 0 to 1. 1 is excluded meaning we just take the first character and that is because H only takes one byte in utf-8 format so ASCII characters in strings usually take one byte while this Unicode characters over here take three bytes so when we take a string slice here we want to access this character now for that we have to provide the right offset so we go from 0 1 2 3 4 5 6 7. starting at index seven and as you can see these Unicode characters hold three bytes meaning we must provide here an offset of 10. 10 is excluded meaning we go from 7 8 9. three bytes iterate through all Source in s so to iterate over characters in a string type we use Stitch Source method but that will actually just return the Char we are iterating over but as you can see we want here as return type a tuple which holds the index and the actual character so we can use here to enumerate method and enumerate will return a tuple with the index and the specific character so in the first iteration you would have zero and H right the index and the character so here we are checking if the index over here is 7 then the variable C should hold this character right let's see and this is compiling now there is a crate called utf-8 slice you can import it into your project and this then allows you to slice into a string not using bytes as offsets but just indexing into the individual characters so over here we provide an offset from four to five meaning we want the character at index 4. so we don't have to worry about the byte size of this symbol then fill in the blanks we have here a string now we have seen that a string is actually a vector holding u8 intervals and as you can see we have here some bytes in a vector so this Vector holds u8 inters right and that means these are bytes that can be converted to ASCII characters so in this case these over here will translate to hello all right if you look up this code over here in the ASCII table then we will find that this is represented for the character age and this e l l o so we want to turn a bytes Vector like this into a string so we want to be string here so let's see this method here so in the documentation you can see that the string type implements a method from utf-8 and this just takes a vector of bytes and then we can call this Associated function on the string type and provide it as an argument a vector of bytes and this will then convert it to a string so we can try it out so first of all we want to actually mutate the string here to hold the same data as this Vector holds namely hello so we can do that by using poster and we push hello meaning this string then holds hello and then we can take this string type and call from utf-8 method providing it this vector and this should then convert this Vector of bytes into an actual string meaning s and S1 will then hold the same value namely a string hello and of course this actually and this returns a result so we have to unwrap it and as you can see this is compiling so in fact a string is just a vector of bytes right and we'll cover vectors very soon and you will then see how this is actually stored in memory so a string is made up of three components a pointer to some bytes a length and a capacity the pointer points to an internal buffer string uses to store its data the length is the number of bytes currently stored in the buffer always stored on the Heap and the capacity is the size of the buffer in bytes as such the length will always be less than or equal to the capacity we have seen that the string type is a three bird object holding a pointer to the actual data stored in Heap memory the length which shows the length of the string in this case 5 and the capacity and the capacity as you can see here is the total amount of memory received from the allocator so when we allocate a new string type on the Heap the allocator we will find an empty spot and then we'll return a pointer to that location number six if a string has enough capacity adding elements to it will not really allocate so when we run this program over here then we see the capacity of string s now over here we are initializing an empty string meaning we have an empty string so nothing gets allocated to Heap memory we still have a string object but with a length of zero and a capacity of zero then we print it out as you can see Zero over here we are iterating two times modifying this string so in the first iteration s will then hold hello so it would have a length of five but when we print out the capacity it will show 8. so we have a string object that points to this data with a length of 5 and a capacity of 8. now this is the default behavior when we don't specify a capacity ourself so when the length exceeds the capacity then rust has to reallocate the data meaning it has to find a new spot in Heap memory that is big enough to hold the data and then we'll return a pointer to the new location so in this case because we have modified the string the length 5 exceeds the capacity zero so it has to reallocate because remember in Heap memory the data is allocated as contiguous block of bytes all right and the default behavior is that the capacity will always double so it starts from 1 then 2 4 8 16 and so on and that's the reason we have here a capacity of 8 even though we only need five right this is a length this is a string of length five but because of the default behavior of the allocator we will get back a capacity of 8. and then in the second iteration we again push hello right so we would have a length of 10 but as you can see the capacity doubles so 10 exceeds the length of 8 meaning the allocator will then find a new spot in hit memory to allocate this string over here of length 10. and it will then Reserve 16 bytes of memory in Heap memory and so on if we then exceed 16 bytes then it will allocate to a location which holds 32 right it always doubles and this is when you let rust allocate so this is the default Behavior but as you can see this might be expensive so if you start with a capacity of zero and then you modify the string multiple times it means rust has to reallocate your daytime and that means it could potentially be expensive when you mutate this over here multiple times and a lot of reallocations are happening so there is a function called with capacity and then we can Define over here that we want an empty string meaning the string length is zero but we want a capacity of 25 meaning s is allocated in Heap memory and there is enough space reserved for this data to hold 25 elements right so when we do that again then as you can see no reallocation has happened we have only allocated the string one time in heat memory with a capacity of 25. now because the length doesn't exceed the capacity in any of this modification of the string it means that there is no need to re-allocate the data right so when we print out swing s over here in the end then as you can see this is the string that in the end hello hello so we have here 10 elements meaning there is no need for reallocation because a capacity of 25 can hold the string of this size and this can make your program a lot faster so let's see vectors a vector is like an array but dynamically sized meaning they can grow and Shrink it's allocated on the Heap as contiguous block of memory all elements in a vector have to have the same type and there is a special macro vac which you can use to initialize a new vector so all this time we've been dealing with an imposter so in actuality this string type is just a vector of utf-8 bytes that gets allocated in Heap memory so as you can see when we have the S1 variable over here then again S1 won't hold the actual data but a pointer to the data allocated in Heap memory alongside a length and a capacity meaning S1 is a three word object of size 24 bytes now as you can see this would be a vector allocated in Heap memory that holds the utf-8 bytes over here of course in memory this would be in binary but as you can see this then holds the character code for this character inside the string so let's do some exercises vectors or resizable arrays like slices their size is not known at compiled time but they can grow or Shrink at any time so over here we have a normal array holding three u8 elements now we can convert this array to a vector using the from Associated function and then passing it the array meaning we will then hold a vector of u8 elements and here you can see the difference between an array and a vector we don't provide here the length of the vector because the vector can actually grow or Shrink meaning it's dynamic in its size now the isvac function basically just checks if the provided argument is a vector then as you can see we are shadowing V and we are here initializing it with a vector of these elements so as you can see you can use the VAC macro to quickly create a new Vector like that so this will then hold a vector of u8 elements and by the way you can either use square brackets or you can use parentheses it's both exactly the same now as you can see here we are initializing V or basically shadowing it and then we are passing it to is back meaning ownership would be transferred but we are using V here again so let's clone this and over here as you can see we are initializing V1 with Divac macro now we are passing to the back macro the array Mac macro over here won't convert it directly to a vector like this from Associated function did instead we would here have a vector holding this array all right I hope you can see the difference here we have a vector of u8 elements over here we have a vector holding an array of three u8 elements so when we compare over here V and V1 they won't actually be the same right and again let's do clone here just so we can then reuse B1 now what we can do over here to fix that is using the VAC new like that and back new basically will then create a new empty Vector meaning it would be an object with length of 0 and capacity of zero now over here we want to iterate over the elements in v let's take a reference here because we are using V again and we don't want this for Loop to take ownership now all we want to do is pushing all the elements from we into V1 and because we are here iterating over references of elements we have to de-reference it and by the way we will take a look at that in much greater detail so what we also have to do is making this V1 variable mutable because when pushing to it it has to be mutable alright let's see and this is compiling so in the end V1 will hold a vector of U 8 elements meaning V and V1 are exactly the same but remember when you have an array use the from Associated function and not the VAC macro because it won't convert it directly to a vector instead it will create a vector holding an array Evac can be extended with extend method so over here we have a vector let's say these are i-32 values and as you can see we can pop meaning removing the last element so over here the vector will then hold 1 and 2 because the last element got popped then we are pushing to it 3. meaning we have a vector holding one two three now over here again we are initializing an empty vector now as you can see we want V1 and V2 to be exactly the same so what we can do is using the extent method and providing it the V1 vector meaning the elements foreign and we have to pass here a reference meaning the elements then from V1 will be put into V2 so again V2 will then be a vector of I 32 elements and they hold exactly the same data right it's basically copying the contents of a vector into another vector turn X into back fill in the blanks so over here we have an array and this array holds i32 types and the size of the array would be 3. then as we've seen we can use the from Associated function so we create a vector from this array meaning V1 will then hold a vector of i32 types or what we also could do is calling the into method and if a type implements from then it will Implement into and we will take a look at that so we then take this array over here and convert it into a vector of i32 values right so that means V1 and V2 will be exactly the same now converting a string into a vector so the from trade over here is actually implemented for the vector type so over here we have a string and remember basically a string is a vector of u8 elements and then again we can call here into right converting s into a vector of u8 elements and then over here we have again a string and we call the method in two bytes meaning V2 holds a vector of utf-8 bytes represented as u8 and this is actually exactly the same so meaning V1 and V2 will be identical then over here we have a string literally and we want to create a vector from this variable meaning we again use the from Associated function and provide it as an argument over here meaning B3 will then hold a vector of u8 types right so V2 over here and V3 are identical iterators can be collected in two vectors so when we have here an array of 10 elements of value 0 and we put it into an iterator we can then collect it into a vector all right and we will see these methods over here in Twitter and collect in much greater detail meaning V4 will then hold a vector and of 10 times 0. like that all right let's see and this is compiling fix the error and implement the code so let's see over here we have an array and we convert this array into a vector using the from Associated function we have seen so this will then hold a vector of I 32 types all right now then we are iterating here five times and just print out each element at the specific index here we have to implement some code and actually V in the end should hold a vector with these elements so as you can see we have we are starting with a vector holding one two three and we end with a vector 2 3 4 5 6. now what we want to achieve here is we are adding one to each element then we would have 2 3 and 4. now if we exceed the boundary over here then we want actually to add the new element over here now let's see first in the for Loop here this would actually panic because we are trying to access indexes that are out of bounds so over here we are iterating from 0 to 4 right 5 excluded but we have only three elements meaning meaning indexes 0 to 2. now we can here you stick get method and we have seen this get method before and and this is generally safer because it will return an option in this case option i32 because we are dealing with i32 types here and what we then can do over here we want to actually manipulate this Vector so let's first access the elements and as you can see again I use the get method because we want to actually see if the index is out of bounds or not so in case it's not out of bounds we would return a sum right the get method will return a sum and the actual value at that specific index so if we found an element at the specific index over here then we want to access the element and just add to it one right we are then taking the element over here let's see for example in the first iteration I will be zero then we pass 0 to this get method meaning we will get back some 1 right so we'll get back some 1 meaning e over here will then hold a value of 1. then we access this array at index 0 right and what we want then to do is taking 1 E over here and add to it one meaning then the first element in this Vector has been modified to then hold two now in the second iteration to do it would be exactly the same thing so I would be 1 we get over here the element at index one and E will then hold two and then over here V at index 1 should then hold two plus one right and we assign it meaning in the modified vector the second element will then hold three now we do that again and after we are out of boundaries meaning we return a non here and then we want to push to the vector the index plus two right so for example in the third iteration I would be three right this would be out of bounds there is no index 3 here so we get back a non and then over here we push to V the index in this case 3 plus 2 meaning we then get back 5 right and let's see if this is actually working all right and let's remove this type annotation because this would be of Type U size because of the get method now as you can see when we print out here the output of the get method we would get someone sum two and some three and then two times none because at iteration 3 and 4 we would get back none because there is no element in this index and we are succeeding here because again we have three times sum here and when we have a sum we just add 1 to the value meaning we then have 2 3 and 4. and then we will get back two times none meaning we take the index and increment it by two pushing it to the vector so we get five and six slicing a vac can be mutable on the other hand slices are read only objects to get a slice use reference in Rust it's more common to pass slices as arguments rather than vectors when you just want to provide read access the same goes for string and string slices so over here we have a vector let's say i32 then we are taking a slice from V over here meaning this would then be a slice of i32 elements right and this would be the offset meaning we would take from the first element to the last element out of bounds will cause a panic you must use the length here so as you can see again we are taking a string slice foreign but we are here out of bounds right because this would iterate from 0 to 3 for excluded and we have here only zero one two indexes so what we can do is using B dot length meaning we just iterate meaning we provide an offset from zero the first element to V length where the length here is excluded meaning vlanth minus one all right slices or read only node slice and reference to back are different so as you can see we take here immutable reference to a vector and then we can push to this Vector over here the value 4. now we have seen that slices are immutable they are just a view into some data so we can't use a slice to mutate the vector right so over here we want to have a slice of i32 elements right like that and we can remove the mute keyword over here because again we can't modify slices so we have pushed to it for meaning when we take the slice we want to take also this last pushed element right and this is compiling capacity the capacity of a vector is the amount of space allocated for any future elements that will be added onto the vector this is not to be confused with the length of a vector which specifies the number of actual elements within the vector if a vector's length exceeds its capacity its capacity will automatically be increased but its elements will have to be reallocated for example a vector with with capacity 10 and length 0 would be an empty Vector with space for 10 more elements pushing 10 or fewer elements onto the vector will not change its capacity or cause reallocation to occur however however if the vector's length is increased to 11 it will have to reallocate which can be slow for this reason it is recommended to use the width capacity function whenever possible to specify how big the vector is expected to get and this we have already covered with strings but now let's see with vectors so over here as you can see we are initializing an empty Vector but with a capacity of 10 so this object here would hold length of 0 and capacity of 10 meaning the actual elements inside Vector are zero there are no elements but in Heap memory there were 10 locations allocated for this data right the vector contains no items even though it has a capacity for more so when we call the length method over here we would get back zero right because back doesn't hold any values but it has a capacity of 10 because we have defined that over here these are all done without reallocating so we are then pushing 10 times to the vector right now this means back here holds enough space that this is happening without re-allocating the vector right so after pushing to the vector T Vector with would be of length 10. and capacity 10. right so the length must never exceed the capacity otherwise a re-allocation is happening as we can see here but this may make the vector reallocate so we are pushing another element to the vector meaning the length will be 11. this would exceed the capacity meaning then this whole Vector will be reallocated in Heap memory so when we have a length of 11 then the capacity will be at least 11 but it can be more because we have seen that the default behavior when reallocating is that the capacity doubles feeling an appropriate value to make the four done without reallocating so as you can see we have here a vector we want to initialize with a certain capacity then we are pushing 100 times to this vector meaning the length here will be 100 so what we want to do is we want here to initialize a vector with a capacity of 100 meaning we can push 100 times without this Vector to be reallocated in Heap memory store distinct types in Vector the elements in a vector must be the same type for example the code below will cause an error so we have here integers and a float this would cause an error because again all of them have to be of the same type but we can use enums or trade objects to store distinct types so over here we again see the IP address enum we have seen before in the examples now fill in the blank we want over here a vector that holds IP addresses meaning at index 0 we want this variant here and at index 1 we want this variant now we can use the back Macro for that and then over here I just copy this variant and this over here right we would then have over here a vector holding types of IP address and because this is a custom type we have to actually Implement partial EQ here so I hope you get the concept we have here a vector holding types of IP address right then over here we have a trade IP address that defines one method display and then we have two Tuple structs V4 and V6 we implement the IP address trade for v2 struct and V6 tract meaning we have to implement the specific method and all it does it then basically prints out the value that is stored inside destruct so fill in the blank as you can see we won't hear a vector holding trade objects remember a trade object is boxed or put behind a reference so over here we have a vector holding boxes right and the Box holds a trade object meaning again this is using Dynamic dispatch and we won't hear a type that implements the IP address straight right in this case B4 and V6 both are types that Implement IP address and as you can see even though we are holding here different types we can use it using here trade objects to store it in the same vector typo so and then we are iterating over the vector and for each element in here we just call the display method that has to be implemented because we only have types in here that implement the IP address trade and this is exactly where Dynamic dispatch happens and we have covered that let's take a look at hash Maps so a hash map is a data structure to store key value pairs it's allocated on the Heap as it is dynamically sized meaning it can grow and Shrink it allows for efficient lookup insertion and deletion of data so when talking about time complexity this means accessing and modifying elements or entries as they are called in hashmaps would be in the best case at constant time meaning Big O of 1 while in the worst case if you have a lot of collisions then it would be still linear time which is still pretty fast which means Big O of n each key is hashed to a unique index in the underlying array so there are vectors stores values by an integer index hashmap store value spy key it is a hashmap implemented with quadratic probing and simd lookup by default a hashmap uses a hashing algorithm selected to provide resistance against hash those attacks so over here we find some information about the used algorithm for the hash Maps but let's dive into the exercises so the first thing is we actually have to use here a namespace so we are basically importing from the collections module from this standard Library the hashmap type and when using vectors or strings these are all in the Prelude meaning we don't have to import it but a hash map you will have to basically import so over here we are initializing an empty hash map then we insert some entries and again a hash map holds entries of keys and values now important here is that all the keys must be of the same type and all of the values must be of the same type so this over here would cause an error and let's go with integers like that and we annotate the hash map like this where key stands for key and v stands for value so over here we use string literals as keys and i32 integers as values then as you can see we can access the entries in a hash map by using again the get method which returns an option we have seen that before when dealing with vectors so we provide the get method the name of the key all right and then we will get back the appropriate value in this case 98 because we are accessing the entry sun phase right so we would get back here a sum an option i32 right this would be the return value some 98 and this would be the type option i32 so we can check if the hashmap contains a certain key so for example if the hash map contains the key Daniel this would evaluate to true meaning the code block here will be executed and we can also index into a hashmap using bracket notation like that so again this would then hold the value of the entry Daniel it would be 95 right this would be an i32 again notice the difference between indexing like that or indexing using using get method we will get back the value here directly while here we get back an option so get is generally safer and then over here we are removing Daniel from the hash map right so we can use the remove method and provide it the key the entry we want to delete meaning after deleting Daniel over here the length of the hash map would be 3 right and we can also iterate over a hash map so we provide it here the variable holding the hash map and then the return would be a tuple holding the key and the value and then we just print out the key and the value let's see all right and we would get back here an option holding a reference to i32 all right and over here too we would get back a reference this is what the get method will actually return so as you can see we are printing out the hash map and this would be the key for example Ashley and this would be the value as you can see now notice that the order here is different from what is over there and that's because a hashmap is unordered so the order won't be retained all right and of course we have only three elements here because we have removed the entry of Daniel then over here we have an array holding tuples which holds a string literal and an integer right so we have an array of three elements and each element is of type Tuple and inside the Tuple is a string literal and an i32 then over here we are initializing a new empty hash map then we iterate over here through the elements of this team's array so for each element here we insert into this empty hash map here the name over here the string and the value all right Implement team map 2 in two ways tips one of the approaches is to use collect method so we can also implement a hash map from an array holding tuples of keys and values by using the hash map from function so we provide it here teams and this will then directly convert this array holding tuples of keys and value pairs to a hash map meaning we then have a hash map holding string slice keys and i32 values all right and over here we would have the same thing and then when we compare teams map 1 and teams map 2 they should be equal let's see this is compiling now the second approach would be taking this teams array here and we want to put it into an iterator and again we will cover that and then we call collect now when we call the collect method it will basically collect the elements in this iterator into the type we have annotated here again this would compile because we have in teams map 2 the same keys and values as we have in teams map one and as you can see we don't need to write a full-blown for Loop we can just use some simple methods let's see number three fill in the blanks so type inference lets us omit an explicit type signature which would be hashmap string slice and u8 in this example so again we don't have to annotate the types I do it just to make things more clear but we can leave it at that insert a key only if it doesn't already exist so here we are accessing player stats this empty hashmap and we call the entry now the entry checks if an entry exists with this specific key and then we can call over here the or insert method meaning if this entry doesn't exist then we insert this key with this value meaning when we then access player stats with a key of Health we would should get back 100 right because this has been inserted over here insert a key using a function that provides a new value only if it doesn't already exist so again we are trying to access player stats and look for an entry Health now in this case this exists right because we have inserted it at that point now we can also use here the method or insert with the difference to our insert is that we don't provide a concrete value but a function pointer meaning this function will then get executed so this function only returns 42 so that means if the entry Health doesn't exist we insert this key with the value of 42. now again Health exists meaning when we Access Health we will still get 100 right because this only executes if N3 isn't found ensures a value is in the entry by inserting the default if empty and returns immutable reference to the value in the entry so we are here checking for the entry health and if it doesn't exist we insert 50 right so in this case Health already exists meaning we would get back immutable reference to the existing value 100 right so this would be immutable reference to 100. and because it is mutable we can then modify the entry manually so we can hear decrement from Health which holds 100 we decrement 50 meaning the health will then hold 50 right this health variable holds 50. let's see this would be u8 in this case and this is compiling so as you can see when we call entry it will give us back a mutable reference to the value if it already exists to the existing value if it doesn't exist it will here create an entry because this is what the or insert method does and will return immutable reference to the created value and then we can modify this mutable reference right requirements of hashmap key any type that implements C EQ and hash trades can be a key in a hashmap this includes pool intu in string and string slices note that F32 and f64 do not Implement hash likely because floating Point Precision errors would make using them as hashmap Keys horribly error prone all collection classes Implement EQ and hash if their contained type also respectively implements EQ and hash for example a vac holding a type T will Implement hash if T implements hash so if the elements in a collection for example a vector or a tuple or an array implement the hash trade then the collection itself will Implement hash let's see fix the errors tips derive is usually a good way to implement some common use traits as you can see we have here a struct viking with two fields of type string and here we have an associate function for Viking just creating a new Viking instance from the arguments we provide and as you can see use a hashmap to store the Vikings Health points so we are creating here a hash map from an array holding tuples of keys and values now notice we have here a hash map and the type of the keys would be the custom type y King all right the value would be i32 use derived implementation to print the status of the Vikings so here we are taking this created hash map as a reference and we would get back a tuple holding the key and the value and then we just print out the key and the value here so we have seen that in order a type can be used as a key in a hash map it has to implement EQ and hash so let's Implement that let's also use the partially queue over here and debug so we can print out the key over here in debug notation let's see and of course we have to implement the hash trade and as you can see it will print out each element in the hash map we have created so we have now a hash map using as key our custom type y King as you can see this would be the key this would be the value of the hashmap capacity like vectors hash maps are growable but hashmaps can also shrink themselves when they have excess space you can create a hash map with a certain capacity using hashmap with capacity or use hashmap new to get a hashmap with a default initial capacity this is recommended so like we did with strings and vectors we can Define when initializing an empty hashmap the capacity right ensuring that when we know we will push a lot of entries that there are no reallocations because remember we allocations are slower so then we are here inserting two entries and as you can see this would be an entry of key i32 and value i32 so the key entity value can be of the same type and over here we insert again another entry indeed the capacity of hashmap is not 100 so we can't compare the equality here so even though we have here defined that the capacity should be 100 it could actually be more than 100 and this is just in case there are collisions there are some internal stuff going on so the RAS compiler will actually allocate more capacity than 100 we defined here shrinks the capacity of the map with a lower limit so when we call map over here our hash map holding two entries and then we can shrink the capacity to 50 right so again the capacity won't probably be exactly 50 then but around 50 so we can shrink the hash map and shrinks the capacity of the map as much as possible so because over here we have only a hash map of two entries 50. spaces in Heap memory would actually be more than we need so we can use this shrink to fit method so that the capacity will shrink around the amount of elements it actually holds again it won't probably be 2 exactly but around this number ownership for types that implement the copy trade like i32t values are copied into hashmap for owned values like string the values will be moved and hashmap will be the owner of those values fix the errors with least changes don't remove any code line so over here we have initialized a variable with a type of i32 and an empty hash map right then we insert here and N3 into M1 and using the same exact value for the key and the value meaning we have here a hash map with keys of type i32 and values of type i32 over here we have a string and we initialize here an empty hash map then over here we are inserting a new entry using V2 this string here as the key and V1 over here as value so we would have here a hash map using keys of type string and value of type i32 and because this is an owned type meaning it doesn't Implement copy this will then actually be moved into the hash map so we can't use V2 here but what we could do to fix that is we could use a string slice of this string right we just provide a reference meaning then the hash map will have type for the keys of string slices right and that means we can then again use V2 and again because V1 is of type i32 it means it implements the copy trade meaning even if we pass it as a key in the hash map or as a value it is still usable but when you are dealing with types that don't Implement copy the ownership will get moved meaning hashmap will then be the owner of the type alright and there are some third-party hash lips but I won't cover that in this beginners course so we'll see you in the next topic let's now look at type coercion and we have already seen the S keyword and this is mostly used for converting integers so type conversion also called type casting is coercing a primitive type that can be performed by as keyword as conversions can be Chained and when casting to an unsight type T for example then T Max Plus 1 is added or subtracted until the value fits into the new type and using unsafe methods can lead to undefined Behavior so let's see an example as you can see we have here the largest unsigned 8-bit integer 255. and this would be the binary representation this means that u8 right this data type for unsigned 8-bit integers Max meaning 255 plus 1 will be 256 so for example if we Typecast 1000 s u 8 then let's see what the compiler will do it will subtract 256 this value over here from 1000 and then it will check seven hours 744 is still bigger than the largest value it can represent meaning it will do the same thing subtracting 256 will leave us with 488 still bigger than this value so again the same thing it will subtract 256 and this will leave us with 232 meaning this will then be the returned value when we cast 1000 s u 8 because remember an 8-bit unsigned integer can't represent 1000 it's out of bounds now when we Typecast -1 as u8 then remember an unsight integer cannot represent negative numbers meaning what it will then do it will basically rotate so 0 would be the smallest possible number an 8-bit unsigned integer can represent and then -1 will just basically rotate it will go back to the biggest one meaning this type casting over here will result in 255. so convert by S rust provides no implicit type conversion coercion between primitive types but explicit type conversions can be performed can be performed using the as keyword fix the errors and fill in the blank don't remove any code so over here we have an F 32 float right then we Typecast this float into an u8 meaning over here the fractional part will get removed so the value would be 97. and of course this will then hold a u8 now we want to Typecast this decimal into HR now this is not possible directly but we have seen that we can chain as statements so we can convert this first to an u8 meaning we will get 97 like we did here and then Typecast it to a character so C1 will then hold a character and by the way 97 would be the ASCII code for a and over here as you can see we can Typecast this integer holding an u8 type as Char directly so C2 would also hold HR now as you can see we are here comparing the integer holding 97 with this type casting now this type casting over here converts the B character to its u8 representation now B would be represented as 98. so what we can do over here is adding one 297 because remember 97 would be a meaning when we add 1 to 8 we will get back B right so both of them will hold 98. by default overflow will cause compile errors but we can add a global annotation to suppress these errors so as you can see the largest possible number u8 can represent would be 255 meaning this would cause an error because again 1000 is larger than the largest possible number a weight can represent meaning we have here an overflow error now we can actually allow overflowing by using this attribute so distance won't complain right and then when we print out B as you can see we would get back 232 because we have seen over here that this would be the result when the compiler performs this operation so we allow here overflowing literals right if we didn't if we comment this out then the compiler will complain because this is actually not possible when casting any value to an unsight type T T Max plus one is added or subtracted until the value fits into the new type that's what we have seen so in this case 1000 as u16 would return 1000 right because a u16 can represent a number of 1000 but over here we would have an overflow meaning this would return 232 like we have seen over here for positive numbers this is the same as the modulus so this would actually be the same as uh doing a thousand modulus 256 remember u8 Max plus one meaning 256. now we have also seen in the example when we have -1 and casting that to an unsigned integer it will then basically just rotate so this will return 255. since rust 1.45 the S keyword performs a saturating cast when casting from float to int if the floating point value exceeds the upper bound or is less than the lower round the The Returned value will be equal to the bound crossed so in this case we have a float here casting it to u8 meaning a saturating cost will be performed so it will just return the largest possible number u8 can represent which would be 255. now in case we have here A minus 100 as floating point and convert it to u8 this just would return 0. it's a possible number u8 can represent this Behavior incurs a small runtime cost and can be avoided with unsafe methods however the results might be overflow and return unsound values use these methods wisely we can use here in an unsafe block the method to int unchecked so the compiler here won't actually check what's going on so as you can see we are casting 300 floating Point 2 and U int to a u8 so as you can see we are here casting 300.0 floating point to and u8 integer and this won't be checked by the compiler that's because it's in an unsafe block now this would return 44 right because 300 minus u8 Max plus one would be 300 minus 256 meaning we would be left with 44. and minus 100 Su 8 as you can see we are typecasting this floating point 2 and u8 integer and again this is unchecked so again what the compiler will do here it will take u8 Max plus one meaning 256 and it subtracts 100 meaning we would get 156. and not a number would be converted to zero okay thank you and of course here we have to use again the attribute allow overflowing literals just so this will actually compile because usually this won't compile and as you can see this would be the output now over here we are dealing with raw pointers and we won't cover that in this beginner's course because this is more of an advanced topic so see you in the next topic so we have seen type conversions for integers and types from the standard library but what if we want to convert types that we defined basically custom types and rust has for this T from entity into trade so let's see from and into trades are used for type conversions between different types without requiring explicit costs it's part of the standard library and can be implemented for custom types implementing the from trade for a type will give us the intu implementation for the given type for free meaning when we Implement from we implement it into automatically so always implement the from trade and not the into trade let's see here an example we have a custom type number and then over here we are implementing the from trade right so we want to convert from an i32 to our custom type number right and the from trade implements a method from so we take here as an argument an i32 and we will then create a new instance of the number type with the provided value and then return it and that means when we use here the from Associated function on our custom type number and provide it in i32 then this can directly be converted to our custom type right and we can do it in two ways from as we've seen because we have implemented it and I've told you that when we and I've told you that when you implement it from trade T into trade is automatically implemented meaning we can also call the into method on the type we want to convert to but when you use into a type annotation is needed so the compiler knows into what type you want to convert this type right so we convert here a type of i32 into a number and this would be the implementation so the from trade allows for a type to Define how to create itself from another type hence providing a very simple mechanism for converting between several types deformant into trades are inherently linked and this is actually part of its implementation it means if we it means if we write something like this we Implement here so we Implement here the from trade for the custom Type U then we can use U from T right we can then create a u-type from the type T we have implemented the front trade on or we could also use into so we convert T into U the input rate is simply the reciprocal of the from trade that is if you have implemented the from trade for your type then the input rate will be automatically implemented for the same type using the into trade will typically require to type annotations as the compiler is unable to determine this most of the time so for example we can easily convert a string slice into a string as you can see we have over here a string slice and then we can call string and the from Associated function providing it this string literally meaning this will then be converted to a string and we could also use the two string method again this and this is exactly the same and we could also then use the into because remember when we Implement from we get into for free so we can so we convert this my string string literal into a string and again type annotations are needed because the sender library has already implemented this for us input from string slice for string so we can easily convert a string to a string slice and vice versa some implementations of form can be found here so as you can see we have here input from Bool for i32 meaning we can convert Boolean into an i32 so when we have here a Boolean value and call into it will convert this Boolean value into an i32 integer in this case this would be zero zero for false and one for true and again when we can use the into method we can use the from Associated function so we call the associated function on the type i32 and provide it here the Boolean value right so this will then actually be exactly the same fix the error in two ways first Implement from Char 4 maybe you should check the docs mentioned above to find the answer second a keyword from the last chapter so let's see the docs so we want here to convert a character so let's see over here as you can see you can see all the types that implemented from trade so we want to go from character and as you can see we can convert a character to either u32 u64 or u128 as well as string so I 32 is not supported and as you notice these are only unsigned types because it's logical a character can't be negative right it can't hold a negative number so we only have unsigned intervals here so we can so we can then in fact convert HR into a u32 let's say so in this case Char implements so in this case we use here u32 right we can convert from HR to Au 32. meaning when we change this over here this should actually work so we provide here a character called the into method and then we should have a new 32. fix the error in two ways so we can't use the as keyword here this is not implemented but what we can do is let's first use the from method so we can create a string or basically converting a character into a string because we have seen in the documentation this is actually implemented and the second way would be to call a and then into right like we did over here Implement from for custom types from is now included in standard Prelude so there is no need to introduce it into the current scope so we don't have to uh so we don't have to import the from trade it's actually in the Prelude meaning we can use it without importing anything so over here we have a custom type number with one field now we want to implement the from trade for our custom number meaning we should be able to convert from an i32 into a number so when we call over here number from right then we should get over here number this would then create an instance of the number struct and the value field here will hold a value of 30 right so and the same thing is when we would call 30 like that and call into right this should then convert the i32 integer into the number type automatically and remember we only have to implement the from trade because then we can use from and into both of them so let's see so this over here is the from trade and let's take this function signature over here and copy it like that now in this case self refers to number right and what we want to do is create here a new instance and we just take the value in this case the i32 right and we provide it as a value for the value field and again because the name of this argument matches the name of the field we can use it like that and this is sufficient so let's see and this is compiling so as you can see then it's really easy to convert one type to a custom type we have created when performing error handling it is often useful to implement from trade for our own error type then we can use question mark to automatically convert the underlying error type to our own type now we didn't cover that this will be the next topic but let's see the exercise we have here an enum CLI error now the two variants here IO error and parse Arrow hold Arrow types from the standard Library okay and these are actually the error types that get returned in case of an error when using these specific methods that are implemented in this standard Library so as you can see we want here to implement the from trade for different error kinds from this standard library and we want to convert that to our custom error type CLI error so let's see over here we have the function open and pause file it takes a file name as argument and it returns a result and we will cover results so a result basically says in case of success it will return a result with i32 and otherwise CLI error so then we call the read to string function and as you can see this is defined in the fs module FS is used for file manipulation and we provide to this function over here the file name now we pass a reference because this is defined in the documentation of this function we are here using the question mark operator meaning in case of success the result type that will get returned will be unwrapped and contents will hold a string in case of an error it will return the CLI error meaning the rest of this code won't get executed and we just get acli error instead now the error type returned from this function would be IO error so in order this error to be converted to a CLI error we have to implement for this error type the from trade right but let's see what's happening here we then take the contents meaning this string over here that gets returned in case this function call is successful and we then hold over here the contents of the provided file then we trim and then we pause now pause could potentially fail and parsing is basically converting a string to an integer value right so in case of success we will get out an i32 in case of error we'll get back a num pulse and error this is defined in the num module again we will cover error handling and all of that don't worry this is now just about the type conversion and then we return an OK with the num value here we have to wrap it in OK because we return a result type now let's see how we would Implement that so again I will just go to the documentation and copy the function signature like that so we have the function signature and then we can over here see what we want to do so in case we get an i o error we want to convert that to our custom CLI error now how would we do that let's change here the argument to e for error and the type would be IO error right we are implementing it from this type now what we want to do is we just want basically to pass the i o error into the io error variant in our custom type so we can access the variant so we can create an instance remember this is the syntax to access the enum so we access CLI error and then the io error variant and all we do then is just passing this IO error type meaning we then have an instance of this variant holding our i o error and that means we have then implemented the from trade for our custom type so any IO error type can be implicitly converted to CLI error meaning in case this function over here returns an IO error it will implicitly be converted to a CLI error let's do the same thing over here for parse int error this would be the error type from the parse method so again let's change here the argument name and the type would be over here and then we again just access this enum access the pause int Arrow variant and we pass it the actual error that gets returned and this would mean we then create another instance of the pause error variant let's see if this works and as you can see this is compiling meaning in case of an error over here both these Arrow types from the standard Library I or error and parse int Arrow will be implicitly converted to CLI error right try from try into similar to from and into try from and try into our generic traits for converting between types unlike from into try from and try into are used for valuable conversions and return a result instead of a plane value now again we use the result type we will cover that when there is a possibility of an error of something going wrong so as you can see we have here an i16 holding 256. into trade has a method in two hence try into has a method try into right this is the method defined on the try into trade so we take this value here and call the try into method on it meaning this will return a result so we match this result in case of an okay then we unwrap the value from the result and if it is an error then we just print out the error over here and return 0. right so in case this succeeds then n will hold a u8 and the value in case this fails n will hold a value of 0. now we have seen that when we want to convert an u8 then this would actually fail because this is out of boundaries but because we are here basically error handling that means that the program here won't actually panic so in case this fails here which it does then we will just assign to n the value 0 and print out the error so in this case n would be zero and again error handling will be our next topic so don't worry if this seems a little bit strange then over here we have a tuple struct even num and it takes an i32 type we Implement here the try from trade for the custom even num type now here we have an Associated type and we Define that it should be of type unit type okay and then we Implement here the try from function so all this function basically does it takes a value and it checks if it is divisible by zero and then we will return the result in this case okay even num and inside it t value provided otherwise if it is not divisible by 2 then we return an error holding our unit type we have defined here as you can see the type in case of an error would be this Associated type error meaning a unit type so we have provided that over here so then we can use the drive from function so when we call even num and try from 8 then this would return okay right because 8 is divisible by two so we would get back a result in this case okay even num and T value we have passed right that's what we defined here now in case we try from a value of 5 this would return an error with a unit type inside because again 5 is not divisible by 2. now with try from and try into it's exactly the same like with from and into when we Implement try from then try into is automatically implemented so we can use this try into method here directly so we try here to convert eight into this result right and the result in case of success will will hold the type of even num and otherwise a unit type like defined over here and in this case result will then hold ok even num and the value we have passed eight now when we call try into on 5 then this will actually return error and a unit type inside like we have defined over here let's see and this is compiling see you in the next topic let's now see two more exercises and this time we are converting a custom type into a string so to convert any type to string you can simply use the two string trade for that type rather than doing that directly you should implement the fmt display trait which will automatically provide tostring and also allows you to print the type with print line so again same thing like with from and into if you implement from then into is for free so you usually don't implement this trade directly for a type you implement display and you will get two string implementation for free so let's see we import here fmt and we have here our custom type point now here we are instantiating a point so what we want to achieve here is that when we take the point instance here we have instantiated and converted to a string this should then be the output and we can also use the format macro here basically just providing origin because the format macro will automatically convert that to a string in order to print it out and the output will be the same so let's implement the display trade here so as you can see this is the display trade and it provides one method now let's take it from over here they provide here an example and we can just copy this over here now I remove the lifetime it's not needed and let's implement the fmt method here so as you can see the fmt method provides here a formatter meaning we want to write we can use the right macro here and the right macro works like println or format but the difference is it will write to a buffer in this case f this is the formatter from the fmt module and then we here provide what we want to print out so in this case we want to print out the point is entity values so let's do that and then like in a print Ln macro we provided the values so in this case we want cells dot X and self.y we want to print out the X and the Y field and that's basically it we have implemented the display trade for Point meaning our customly created type Point can be converted directly to a string so let's see and this is compiling so if we pass origin here let's see the output as you can see this would be the output so we can even manipulate how the output should look like so instead of just so instead of just printing out the two fields we have even provided the output with a whole sentence right this will be the last one we will be doing we can use parse method to convert a string into an i32 number this is because from string is implemented for i32 type in standard Library to use from string method you need to introduce this trade into the current scope so we have to import this trade from the stir module so over here we want to pause and parsing again means converting a string into an integer so we would here have an i32 type of this value and over here the same thing now we can either annotate the type over here or we could use the turbo fish syntax meaning we can directly annotate the type on the method like that and then because the from string trade is actually implemented for the i32 type we can also call the associated function from store let's do 20 here so this will convert from this string literal into an i32 meaning this will then hold an i32 now because all of them hold i32 integers we can then do a mathematical operation meaning we just add together all of these values 5 10 and 20. so to be variable will hold an i32 and it should be equal to 35 and this is compiling see you in the next topic now we will cover error handling anti-simples form of error handling is to use the Panic macro Panic will print out an error message unwind this deck and finally exit the program now unwinding the stack means that when you actually execute the program then we have seen that the local variables and the function calls will be pushed to this stack now when panicking that means that the compiler will actually delete everything step by step from this stack memory and then exit the program and in multi-threaded programs it will exit the thread in which the Panic occurs not the whole program so we don't deal here with a multi-threaded programs this will be another course but I just want to mention it so let's see exercises as you can see we have here a function drink which takes a string literally if the string literal is lemonade then we print out success otherwise we actually want to panic right because we want to ensure that this line never gets reached meaning never gets printed out so when we pass over here lemonade then this will print out success and then will panic and this would be the output if a program panics threat main panicked at explicit panic and it will provide you the line number at which the Panic occurred in in this case an explicit panic because we have here called the Panic macro common Panic cases make make the code work by fixing all panics so when we call here the S bytes method on a string then it will give us back the array holding the utf-8 bytes of this string now we have seen that actually the utf-8 representation of a would be 97. B would be 98 and C would be 99 so again if srtq doesn't match if this does evaluate to another value then this then assert EQ will cause a panic over here we have a vector and we try here to access the third index now we only have now we only can go to index 2 right it's the outmost index we can provide so let's change that and this will then hold an i32 right because we are accessing a specific element unwrap May panic when get return a none so the get method over here will return an option meaning it either returns a sum with a value or a non now in this case this would return a non because we don't have any value at index 3. so when we try to unwrap a non value then we will panic now let's hear excess index 1 for example then it will return a sum two right the first element write the element at index one and unwrap we basically just unwrap the inner value so La will then hold a value of 2 of type i32 sometimes the compiler is unable to find the Overflow errors for you in compile time so a panic will occur so when we call here the production rate per hour function with a value of 2 then let's see we have here speed the provided argument of type u8 and here we have cph of type u8 now as you can see we are inside here multiplying the speed in this case 2 by cph 222 now this would actually result in an overflow because 2 times 221 would be 442 right now remember an u8 can only represent up to the number 255 so this would actually cause a panic but what we can do is just actually taking here a u16 and converting that to u16 meaning when we multiply these together then the result of u16 will still be able to represent the resultant number and over here we can do the same thing and then let's see because of the same reason as above we have to wrap it in a function to make the Panic occur so here we are calling the divide function with two arguments 15 and 0. so to divide function takes two arguments of type u8 and just divides it now this would also panic because we can't divide by zero this is a mathematical Rule and this will always lead to a panic so let's here divide by 1 for example let's see if this is compiling all right and again when we use the get method we will get back a reference to the value and not the actual value so let's change that and this is compiling detailed call SEC by default is stack unwinding will only give something like this so this would be the output if your program panicked though there is the reason of panic and the line of code is showing where the Panic has occurred sometimes you want to get more info about the call stack fill in the blank to display the hole called stack tips you can find the clue in the default Panic info so when we run cargo run this is basically how you execute a program you have written and will cover that then you just provide over here this rust back Trace equals one environment variable and it will then give you detailed information about the Panic that occurred so over here unwinding and abort by default when a panic accuracy program starts unwinding which means rust walks back up to stack and cleans up the data from each function it encounters but this work pack and cleanup is a lot of work the alternative is to immediately abort the program without cleaning up if in your project you need to make the resulting binary as small as possible you can switch from unwinding to aborting by adding below content to cargo.com so we can here Define in this cargo.tamil file and I will show you an example of that how Panic actually behaves so we have two um so we have two options here either unwinding or aborting when choosing a board here then the stack won't get unwinded meaning this will then be the responsibility of the operating system and not of the rust compiler alright see you in the next episode so we have seen the option type and the option type stands for a value that could potentially be absent so let's now look at the result type result is an enum type that represents the outcome of an operation that could potentially fail it has two possible variants okay a value T was found and error and error was found with a value e and as you can see the variance hold the specific information the expected outcome is okay the unexpected outcome is error since result is an enum the possible variance can be matched using a match pattern and that is what we call error handling so let's see an example let's again consider the divide function we have seen before so again we can't divide by zero meaning if the second argument here is 0 then we should return an error otherwise the program will Panic now we can do here error handling meaning we check if Y is zero right or in this case 0.0 because the arguments are floating Point numbers now in case y what's the value of 0 then we return the error variant of the result enum and we provide it an error message all right so in this case we use an explicit return keyword meaning when this returns it won't execute any further it will directly return the error type and stop execution now in case Y is not 0 then we will just divide X by Y and wrap it inside okay because remember result is an enum meaning we have two wrapped the resultant value inside the variant so that means when this function succeeds then we will get back an F 32 right the result of this operation and this is wrapped inside okay because as you can see we are returning a result type now in case of an error we would return this string over here wrapped in the arrow variant and this allows us then when we call the function divide with two arguments that we can then match the result we get back here as you can see we would get back a result type meaning the result type has two variants either okay or error and we can here use pattern matching to the structure the inner value for example this F32 into the variable Val which we then can print out and in case of an error we will get back this string over here the error message and we destructure it into this variable here and then print it out unwrap the unwrapped method takes as input a value of type result and takes out the value which is wrapped inside okay in case of success or panics in case of an error in case it returns an OK variant we just take the value that is wrapped inside otherwise the program will panic so you should use unwrap only if you're a hundred percent sure that the return variant will be okay otherwise you should use match and let's see the question mark operator the question mark operator is a shorthand way to propagate errors or unwrap OK results basically it's the same as unwrap but instead of panic it returns an error it replaces an entire match statement and can and it can even be used in the main function so as you can see over here in the main function we can Define here that main should return a result type and as you can see we have here a string holding a number then we try to parse this string over here into an i32 type now we have seen that the parse method could potentially fail right if we have here a string holding T for example the compiler can't convert it to an i32 because it's not a valid number so in case we get back the OK variant remember parse will return a result if we get back okay then we just take the inner value and return it meaning the inner value will then be assigned to the number variable in case of an error we want to return meaning this over here won't get executed it will return before and we just provide the error message over here and in this case because we are using a function from the standard Library this error message is actually provided right so we just take the provided error message and return it and if this didn't fail it means this will get executed and we just print out the unwrapped number now because we return here a result type we have to also return an OK in case everything succeeded now because at the end of Main basically the end of the program has been reached so it doesn't make sense to return any meaningful value so we just return a unit type like that so I've told you that the question mark operator actually replaces an entire match statement meaning this over here would be exactly the same so let's see again we are in Main and we return a result now as you can see we have again a string holding a number then we try to parse over here and again pause returns a result so we have to handle the error case now we can do that appending to it the question mark operator meaning in case this succeeds and it returns an OK with the value in this case 10 but post as an integer then it will just assign it to this variable meaning number will then hold 10 as an i32 in case this fails then it will return a pause int error as defined in the parse method okay so this over here is exactly the same like we did over here and when this returns an error the following lines won't be executed in case this succeeds then we have number which holds 10 of type i32 then we'll just print out the post number and then we return a unit type because remember we are here returning a result meaning we have to also return OK variant in case of success but again because we are in Main and this would be the end of the program we don't return any meaningful value we will return a unit type and there is also a concept called type Alias so it's basically just a way giving a name to an existing type so if you like to write u64 like this then you can create a type Alias meaning we can use the type keyword and provide a new name for a specific type and as you can see over here we are then using the new type we have created here and this would at compile time then be replaced with the real type and please don't confuse that with Associated types in trades they are different okay let's see result is an enum to describe possible errors it has two variants okay and error in short words the expected outcome is okay while the unexpected outcome is error so let's see fill in the blanks and fix the errors first of all we are importing the pause and arrow from the num module because we are using here the parse method meaning it will in case of an error return the parse and error now we have here the function multiply that takes two arguments of type string literals okay now as you can see we are here returning OK meaning the return type will be of type result and in case of success or let's actually wait with annotating that let's first see what's happening here so we are taking this string literal and try to parse it to an i32 right so again this parse method over here will return a result in case of success we would have okay and the value that was paused so for example when we call here multiply with an argument of 10 for the first parameter then we would have a parsed number 10 right so we would have in case of success and I 32 here in case of an error we would have the parse into error like that and parsing terror is the error type that is defined for this method again this is implemented in the standard Library so this has to comply with what is defined there and N2 is exactly the same but for the second argument then over here we unwrap and remember unwrap will take a result and just unwraps the inner value so in case we have okay let's take the first argument 10 then unwrap we'll just unwrap the inner value meaning it Returns the integer i32 right that is wrapped inside the result enum and then over here we are multiplying by N2 again we have to unwrap because this is a result type and if this succeeds actually then we will return an OK meaning OK will then hold in this case 10 times 2 will be 20 right like that so we return an i32 in case of success in case of error we we will return a pause and error because that is the error that is defined from the parse method so let's see when we call multiply with these two string literals 10 and 2 then this should actually work right because the compiler is able to parse these into integers so it will pause here and this will then be okay 10 and over here we would have okay 2. then we unwrap both of these over here meaning we unwrapped the value from the variant and here are two meaning we multiply 10 by 2 and then this would be 20 and we wrap it inside okay because we have to return a variant of the result enum which means that the return type over here would be this right so result in case of success will hold ok and 20. right now in this case this would actually fail because the compiler can't convert T to an integer now we want this to actually assert to 8 so let's do here four and this means this will get passed to four and this will get paused to 2 right and then again we just multiply these together and return it wrapped inside the OK variant meaning over here we get back a result and result will then hold ok and eight right now if we unwrap the result over here then it would unwrap the inner value as you can see so unwrap method allows us to unwrap the inner value so okay 8 would be unwrapped to eight question mark operator is almost exactly equivalent to unwrap but question mark operator returns instead of panic on error so let's see we have again the percent error Implement multiply with question mark don't use unwrap here so let's see again we are taking two arguments of type string literal right three and four in this case and let's start to implement so we declare here N1 and we take N1 string and try to parse it into an i32 right now again we should here use the question mark operator so we append it after the method call here and then we can only use the question mark operator when the return type of the function is a result right because the question mark operator in case of an error will return the error variant and inside it the error value so we return error a result in case of success it should be i32 and in case of failure it should be dispersed in error because again this is defined as the error return value of the parse method now for N2 we do exactly the same and that means if any of these fail then it will immediately return meaning the rest of the function won't get executed and a pause end error will be returned now the question mark operator in case of success it won't return a result it will unwrap so for example when we pass here 3 and this will get passed it will return okay and three right and then the question mark operator like unwrap will unwrap the inner value so N1 will then hold three meaning an i32 integer and N2 the same thing and then all we want to do is just returning and OK in case of success because the return type is result and we want to multiply N1 by N2 notice over here that I didn't have to unwrap because we have here I 32 types right while over here we had to unwrap because this held result types you have to remember that let's see and of course I have to change that over here typo and this is compiling so what we had over here when we provide Forum as a string we would have okay four right and then the question mark operator unwrapistic value and then over here we are multiplying three by four which would be 12. then we wrap it inside ok to comply with the function signature so this will then be the returned value right and then over here as you can see we are calling the function and unwrap it because we have here a result type so we unwrapped the inner value because this would return okay 12. and we unwrap it meaning the inner value gets unwrapped let's see over here we have the file type from the fs module and over here we are importing some stuff from the i o module so over here we are opening a file providing here to the open Associated function the name of the file in form of a string literal meaning this will then return a result and in case of success it will hold a file right and in case of error it would be IO error all right and then we are matching f now F holds a result meaning we must over here handle both cases from in case of success and in case of failure so if it is okay then we unwrap the inner value meaning the file and we just assign it to f in case of an error we return the error wrapped inside the error variant of the result type right because this function over here returns a result all right and then over here we are creating a new empty string we then take F over here and read to string meaning the contents of this file over here will be copied to this string meaning s will then hold basically the contents of the file we have opened and we have to provide here a mutable reference because this is defined in the standard Library and then again because this read to string method returns a result we have to match it so in case of okay then we will return over here a string right this string s over here in case of error we will return the error and both of them file open and read to string return an i o error all right now over here fill in the blanks with one code line don't change any code lines as you can see we have here the function read File 2 which returns a result in case of success it's a string in case of error it will be the i o error from this standard Library we here declare a new string and we here initialize a new empty string and I told you that the question mark operator replaces a whole match statement so we can write all of this in one single line so first of all we are opening the file so let's take this and as we've seen open will return a result so we can use the question mark operator here to replace this whole match statement so in case of error it will immediately return and it returns this error type in case of success then we will get OK and a string and this will then unwrap it to the string right and in case of success we will have a value of type file like we saw over here and again file is a type defined in FS module so we have seen that the question mark operator will then unwrap we will basically get something like that and it will unwrap the file inside the OK variant what we then can do is call the read to string method on it right so we are calling read to string and then again we have to append this question mark operator because we need to string also returns a result type right so in case of success we'll then over here mutate s meaning the contents of this file will get read to this string all right or written to the string and then we just return s right the string and that means in case of success we will have as return type a result that is holding a string in case this or this fails then it will return an i o error meaning if this fails then over here a i o error gets returned and this will never be executed and if this fails again we return this error and this will never get executed let's see this is compiling map and then map and then are two common combinators for result t e also for option t so we have over here the parse into error fill in the blank in two ways map and then so we have here the function at 2 taking a string literal and returning a result in case of success holding an i32 in case of error parse and error all right because this is the default error type of the parse method then we are taking this then we are taking this string literal and try to parse it to an i32 and then as you can see when we provide here at 2 with a string holding four then we want to get back six so as you can see when we provide four we will get back 6 over here and that means we want to parse this string and then add 2 to the post number and for that we can use map which is really convenient because we not only want to handle this result case but also then modify the value right so n here takes a closure and we will cover closures very soon so we provide here an argument n and n would then hold the parsed number and all we do is adding 2 to n right and that's all so let's actually see the map method in the documentation as you can see maps a result te to result u e by applying a function to a contained okay value leaving an error value untouched so when we call the map over here it will perform it will apply a function if this method returns an OK value if it returns an error then it will leave it untouched all right so in this case this would actually succeed right so pause over here will pause this 4 into an i32 integer meaning it will then take the OK value and unwrap it so n will then hold A4 and then we just add and and then we just add 2 to 4. right let's see and this compiles now the second approach would be using and then now and then is very very similar to map the difference is here in the closure that the end then method takes so let's go back to map here so over here you can see this is the argument for the closure right and the closure over here returns directly the value all right and over here it will the closure itself will return a result now both of these methods return results over here but the difference is in the closure and you will see what I mean when we are looking at this so this is very similar it will take this past integer and then put the unwrapped value into n and then over here we add 2 to 2N now we have seen that map would work like that and and then method will actually return in the closure itself a result so we have to wrap that into ok like that but otherwise they are exactly the same and this succeeds number five with the return type we written we can use pattern matching without unweb but it's all verbose so here again we have the multiply function taking two string literals in case of success it returns an i32 otherwise parse and error so over here we are using the match keyword now we want to pause the first string literally the first argument in case of okay then we destructure it into T variable N1 so N1 will then hold the parsed number in case of an error we just return the error type then we match inside this match arm over here again so we match the second argument parsing it and if this is okay then we get out and two all right and then we can multiply N1 by N2 and return the result wrapped in the OK variant because as you can see the function returns a result type and in case of an error it just returns an error so this would be very verbose so rewriting multiply to make it succinct you should use both and then and map here so let's see we take first of all the n one star over here the first string literal all right then we want to parse it to an i32 integer right and let's then use and then so if this succeeds then we will have here n which holds the past integer right and let's actually write it more beautiful like that so we take then n or let's call it or let's call it N1 just to make a distinction and then over here we want to parse the second one right we want here the second one and then if this also succeeds we want to multiply N1 and the post and two together so let's use a map here and we would here have N2 right because map over here is called upon the parse method so we would then get back and two and then we just return N1 times N2 and notice because we are using map here we don't have to wrap it inside okay right so this will then return for example when we call it by 10 and 2 it will return 20. all right and and then we have seen that it will return the result type so in case of success it it will return okay with an i32 value inside otherwise it will return a pause end error let's see and this is compiling so as you can see when we are calling the multiply function with with 10 and 2 as string literals it will pause the first argument meaning we then have over here okay 10. right and then over here we call the end then Method All right so we take this N1 value here basically unwrapping it and inside and then we parse the second number so in this case two right so this will then get parsed into an i32 and we then can map meaning we have N2 over here and two in this case would be okay 2 right and map will take an unwrap the value inside OK meaning we then have N2 which holds two over here and then we multiply N1 by N2 and return it all right and all we then do is here The Returned value would be of this type right and in this case this would return okay 20. right because it could parse these two numbers and it multiplied it and returned it so we get okay 20 then we pass that to the print function as you can see it takes as an argument a result type then we match it if it is okay then we print out the value wrapped inside okay in this case 20 otherwise we will print out the error and when we call multiply with an argument that can't be paused then you would get a pause int error right so this would then hold a percent error we are providing that 2D print function and then we match the argument this TT and this will then match the error meaning the error will get printed so in the first one so in the first print call n is 20 so we have successfully paused these two numbers and unwrapped the value printing it and in the second one because we provide here an argument that can't be paused we get and pause into arrow and this would be the error message that is provided in this standard Library so type Alias using this type over here everywhere is verbose and tedious we can use Alias for this purpose so at a module level creating aliases can be particularly helpful errors found in a specific module often has the same error type so a single Alias can succinctly Define all Associated results this is so useful even the standard Library even supplies one IO result so as you can see we have here again the pulse and error and we want here to create a type alias now we have here the same multiply function we have seen above so I won't cover that anymore but the return type is defined as res i32 and the print function over here takes as an argument a type of rice i32 now as you can see exactly the same functions as we had over here but we don't write out the whole type we use here A type Alias so we can Define that here we use res i32 and we want to Define that as a result in case of success holding an I30 tool in case of an error as you can see the parse method would return a parse and error and then instead of writing that all the time in our functions we can use this simple type alias and of course at compile time these type aliases will then get replaced with this concrete type here let's see and as you can see the same output as before using result in F and Main typically the main function will look like this however main is also able to have a return type of result if an error occurs within the main function it will return an error code and print a debug representation of the error debug trade the following example shows such a scenario so as you can see we have here again the percent error and we Define here in the main function that the return type should be of type result in success returning a unit type in case of error it will return a parse and error so over here we have a string literal and we are then trying to parse this string literal to an i32 now again pulse returns a result so we have to handle both cases in case of ok we just take the wrapped value and assign it to number right in this case this would hold an i32 in case of an error it will just return the error and it will stop execution from this point and then all we do is we will print out the number so let's see if this is compiling as you can see this is compiling we are printing out the number and don't forget because we return a result type here we have to return the result variant okay with a unit type in case of success we provide here a unit type to the OK variant because the end of main means the end of the program because the end of main means the end of the program so we don't have to actually return anything here right because if the program ends this returned value won't be beneficial but we can just provide the unit type so we comply with the function signature and by the way as we've seen we can here just use the question mark operator so we can replace this match statement by this and this is actually the reason most people use here in main the result type because then it allows us that we use the question mark operator otherwise if we didn't have result here we can't use this operator let's see of course I have to remove the match keyword here and as you can see this would be the same thing all right see you in the next topic let's take a look at cargo now cargo is the official package manager and building tool in Rust it helps automate tasks such as creating new projects building running testing code and managing dependencies a crate is a compilation unit of rust source code so basically a crate is the smallest amount of code the RAS compilers considers at a time and crates.io is the repository for rust packages so as you can see this is the website of crates.io and here you find some libraries for example we have here the rent Library and this allows for creating random numbers and as you can see you can add this very quickly into your own project and we'll see how this will work what is a crate a crate can be of two types either it's a binary crate and that means the program is compiled into an executable binary it's basically a program you can run and binary crates always have the main function in it and a library create a library create is compiled into a library and it doesn't contain a main function but it's considered a collection of reusable code that can be shared across multiple projects and the create route is the source file that is the root module of the crate in binaries it would be source main.rs and in libraries it would be the file lib dot RS then we have modules and a module is a way of organizing code by grouping together related items modules can be imported using namespaces avoiding naming collisions it also controls the privacy of its items like functions trucks enums and so on when compiling the compiler starts from the create route we have seen in a binary crate this would be in main.rs and in a library it would be lib.rs then it checks if the modules are declared and looks for sub modules sub modules could be directly written in line with curly braces it could be written in a separate file which has the module name ending in dot RS or it could be in a separate director which has the same name of the module and a mod.rs file inside it and we'll do exercises and you will see how that works so in case you want to follow these exercises you actually have to install rust on your system so you can go to rosslang.org and then you can click install and over here it will give you a command you can copy then running a terminal and you can just copy this into your terminal now I'm on a Unix like operating system meaning I will use the Linux command but the website will recognize your operating system and provide the appropriate command so if you're on Windows it will give you a command that you can paste into Powershell for example so proceed with installation default you can then press 1 and enter now I have already downloaded that so I want um run it again and then to check if everything is installed correctly you can use these commands first of all we check is the RAS compiler installed if you get back a version it means everything is installed then we check for cargo this is fine and rust up and rust up is used for um updating the version of rust on your local system all right all right so let's now see the exercises a package is a project which you create with Cargo in most cases so it contains a cargo.tomel file in it so create a package with below layout now I will do that directly in the home directory but you can do it at any location you want so to create a new package we can use cargo new and then provide the name of the package so we want the name here to be hello package and as you can see that we and as you can see the package has been created so let's C CD hello package and we are now inside our created package right so we have here cargo Tamil and a source directory let's see source and if I print out the contents in main as you can see it contains a simple program that prints out hello world so let me actually do it like that so over here if I now do A3 command as you can see we have exactly the same file structure here we have a cargo Tamil and then resource directory with main.rs inside let's print out the cargo toner and as you can see this matches the file in the exercise create a package with below layout so over here we want to create a library so I'm back in my home directory and we can create the library create right now let's do Park on you then the package name here and then we have to write the lib flag right so this will then create a library and then let's CD into that as you can see we have corgotomel and a source directory now let me print out the cargo thermal file and as you can see again this matches and if we go to the source directory as you can see over here we have a lib.rs file and this just has a basic add function over here together with some tests all right fill in the blank with your answer what's the difference between package number one and package number two now the difference would be package number one is a binary crate and package number two is a library crate all right create a crate is a binary or library to create rules the create route is a source file that the rascompiler starts from and makes up the root module of the crate in package hello package there is a binary create with the same name as the package hello packet and source main.rs is the create root of this binary crate similar to hello package hello package one also has a crate in it however this package doesn't contain a binary crate but a library crate and source lib.rs is the great root so what's the name of the library crate in package hello package one the answer would be it would be hello package one right because as we've seen over here in the package hello package there is a binary crate called hello packet and that means in a package hello package one the name of the library crate would be hello package one and by the way a package is what we have seen over here basically when we create a new project right when we create a new project we create a new package and the package consists of a cargo.tomel file and a source directory together with other stuff add a library create for hello package and describe its files 3 below alright so we want to add in the hello package a library crate so let me remove this over here and we will go inside hello package now over here in this Source directory we want to create a new library create as you can see we have until now a binary crate so let's create a lib dot RS touch is a Linux command that allows to create a new file so then we would have lib RS and Main RS so when we look at T3 as you can see we then have the cargo terminal The Source directory and two crates one Library crate and one binary crate so this will then hold lip.rs and main.rs after this step there should be two crates in package hello package a binary crate and a library crate both with the same name as the package a package can contain at most one Library crate but it can contain as many binary crates as you would like by placing files in Source bin directory each file will be a separate binary crate with the same name as the file so create a package which contains three binary crates hello package main one main two one Library create so what we want over here is to have in this Source folder the lib.rs and main.rs then inside the source folder we want to have a bin directory which holds main 1 and Main two right so so as we've seen we can have as many binary crates as we want in a package but only one Library create and over here we have some other directories so we have tests for integrated tests benches for Benchmark and examples so let's create these over here so as you can see inside source we already have a library crate and a binary crate now we want to create a new directory called bin then we see the into bin and we create main1.rs and main2.rs so again when we go to the root of the package as you can see we have cargo terminal The Source directory we have inside the source directory a bin directory holding two binary crates main one main two and we have in the source directory lib.rs and main.rs yep as you can see the above package structure is very standard and is widely used in many rust projects so let's see modules modules let us organize the code within a crate into groups for readability and ease of reuse module also controls the privacy of items which is which is whether an item can be seen by outside code public or it's just an internal implementation and not available for outside code private we have created a package named hello package in previous chapter and it looks like this now we actually created over here a bin directory so let's delete that I go into source and then remove the bin directory so as you can see T3 looks exactly the same now it's time to create some modules in the library create and use them in the binary crate let's start so as you can see we can Define some code in the library create and then use it in the binary crate so Implement module front of house based on the module tree below so in the library create we want to create a module front of house and this should then hold two sub modules hosting and serving now hosting holds two functions and serving holes for functions so as you can see we will do that in the library crate all right and again modules allow us to structure our code so let's open lib.rs and I will copy this over here so we have to implement front of house now we have seen that front of house module should contain two sub modules namely hosting and serving like that so we have a front of house module that contains two sub modules hosting and serving now we want to add these functions over here add to wait list and see it at table in the hosting module uh at waste list adds to wait list and see it at table so as you can see front of house containing one sub module hosting holding two functions over here now let's see serving I will just copy that like that and doing it like that as you can see it is serving module holds three functions and when we run the code actually we can use the cargo run command and as you can see this is compiling but it gives us warning that we didn't use these functions over here which is okay and of course hello world will get printed from the binary crate right because main just prints out hello world let's call add to waitlist from a function eat at restaurant which is within the library create route so inside lib.rs we are below the module front of house we are creating a new function like that call add to wait list with absolute path so it's like in a file system you can have absolute paths and you can have relative paths so in this case we want to use the absolute path to the add to wait list function over here so we would use for the absolute path to create root then we want the front of house module and we want the sub module hosting right and then over here we can call the function like that add to wait list now this would be considered the absolute path now over here we are using the relative path so we are actually already in the create route right so that means we don't need to use crate here we can directly access front of house hosting and then call the add to wait list function now as you can see it gives me here an error module hosting is private so we can make this public using the pub keyword all right now the same thing with the functions let's make them public like that you can use super to import items within the parent module so again in lib.rs we want to create another module so let's copy that put it below the front of house and as you can see over here we have the back of house module with two functions declared inside it so we have fixed incorrect order and inside this function we call the cook order function over here fill in the blank in three ways using keyword super using absolute path so in order to access the function over here serve order let's first use this super keyword so super means we go one level up right so we are here inside the module back of house we go one level up which would be the create route right lib dot RS so we use super and then we can access front of house and I guess hosting no serving and serve order like that and again serving is private let's do that let's make it a public function and again serving here is private so let's make that public and also the function here like that and the second way would be using absolute path so again you can use here create right create would refer to lib.rs because it is the great root of this package separating modules into different files so as you can see we want to put these modules into their own distinct files please separate the modules and codes above into files resident in below third three so as you can see we have here Main and leap RS crates and here we have the modules we want to split up so we have the module front of house holding hosting and serving this r sub modules and mod.rs and we have over here back of house.rs now we have said that there are three ways to declare modules right the first way would be in the create route for example lib.rs we Define in module and put it right inside the curly braces right all the code is inside these curly braces right the second way would be two in lib.rs declaring the back of house module but without curly braces right only declaring like this for example only declaring the module and that means the compiler will then check is there in Source directory a file named as the same name as we gave it to the module right so we declared a module in lib.rs and then we have to create a file ending in dot RS with the same name as the module we have declared and the Third Way would be if we want the module to be a subdirectory of source then we have to again declare front of house inside lib.rs the same way we did before but we create a directory instead of a file with the same name as the module and then very important the code of the module will then be in mod.rs now in our case we are declaring in mod.rs2 sub modules hosting and serving all right so we want to have this code separated in different files and this is how it should look like so let's start with back of house all right I will copy that and first of all I will delete anything in here and let's declare the module back of house right now we have here declared a module as you can see the file is not found so we can here declare and back of house dot RS and then we can put and then we can put the code of the back of house module directly into this pack of house file removing this over here because we have already declared T module and as you can see back of house file now contains only the code related to this module again we have in lib.rs declared the back of house module then we have over here created a file that has the same name as the module right and in here we are providing the code like that and because we have already declared the back of house module we don't have to redeclare it inside here right now as you can see we also want front of house now the difference is front of house should be a directory holding two sub modules so let's first of all in lib we want to declare front of house module like that then we create the directory with the same name as the module foreign like that then the first thing is we create a file mod.rs this is now the create root of this this will be considered the root of this module mod.rs and as you can see we want here inside front of house two sub modules hosting and serving let's declare them like that and then we can create both of these files let's start with hosting and we just take all the code from the hosting module here like that and let's create serving and we just take the code here from this serving module like that let me remove the comment so and now if we go back to the source directory as you can see we have back of house this is a module we have front of house hosting and serving these are two sub modules and we have here mod.rs exactly like we wanted here now over here by the way this function eat at restaurant will be directly in the lib.rs so as you can see instead of defining the module and putting all the code in curly braces meaning inline we can use um different files to restructure and reorganize our code and this makes the lib.rs file much more clean and readable assessing code in library create from binary crate please ensure you have completed the fourth exercise before making further progress you should have below structures and the corresponding codes in them when reaching here so we have done that now we will call a few Library function now we will call a few Library functions from the binary crate so let's copy that into the binary crate like that now we want to call a function so the output should be sit down please and then a second function which returns yummy yummy now as you can see over here we have to call the eat at restaurant function to get back yummy yummy and sit down please would be in the front of house module in hosting and seat at table function so let's see first of all I want here the module front of house right because the binary crate and the library create are separate from each other so the binary crate over here can't know what is defined in the library crate and then over here we can SD create module and then here we can use an absolute path providing the create route now we have to actually provide here the name of the crate meaning hello package all right and then we can call font of house and let's see front of house hosting seat at the table at table like that and then over here we call hello package and then eat at restaurant this is defined in our lib.rs so again instead of writing here a crate we write the name of the package because we are here in the binary crate so we can use the create keyword in lib.rs but in main.rs we have to write the name of the package let's run that and as you can see we have some errors module serving is private and module hosting is private so to actually access that from the binary crate we have to make them public so let's see first of all we go to lib.rs and we want these two modules to be public so let's make them pop like this which means that these two modules are now accessible for our binary crates the main.rs file then let's go to um front of house and let's here make these two modules or these two sub modules also public like that and if the module itself is public that doesn't mean that the inner functions for example or structs or anything that is inside the module is also public so we have to declare here as you can see this is already done that these functions are also public if we want to access it and as you can see serving also has the public functions now let's check again in lib.rs because we are calling here a function all right this is also public let's run it again and as you can see this is actually compiling it just over here and complaints that we didn't use some of the functions which is okay at the moment now instead of just assorting let's actually print that out and let's do it like that as you can see the output will be sit down please let's call the second one so we want to get this like that let's see all right I copied here the holy uh I copied here the whole assert we don't need this actually let's see and as you can see the output will be sit down please and yummy yummy so we have successfully restructured and reorganized our whole code into different files and folders meaning it is much more organized and much more readable all right see you in the next topic we will now take a look at the debug and display trades so types which want to be printable must Implement either debug or the display trades and automatic implementations are only provided for types in this standard Library so this doesn't apply to our customly created types a debug trade can be implemented by simply using derivable trade and a display trade must be manually implemented let's see we have here print Ln and format printing is handled by a series of macros defined in standard format some of which include format write formatted text to string print same as format but the text is printed to the console print Ln is the same as print but a new line is appended ePrint is the same as format but the text is printed to the standard error and e-print Ln same as e-print but a new line is a pendant all parse text in the same fashion all porous text in the same fashion as a plus rust checks format correctness at compile time so let's see we have here a string literal and we use here the format macro meaning the return type will be string and we want here s to hold a string of hello world so again we use the format macro like we would use the print L and macro we have been using so in this case we want to take S1 over here and then just append the rest so we take hello this will be placed here and then the rest and this is compiling let's actually print that out as you can see this would be the output when printing out s print print Ln fill in the blanks to make it print a hello world I am and turn on the new line Sun face so over here we want to use print because as you can see I am should be on the same line so as we've seen print macro doesn't append a new line character now a new line is defined in C like that all right and this is a symbol that the compiler knows that it should treat it as a new line but in this case print doesn't append it so the next line will be on the same line as this print statement so let's use here print Ln because as you can see after I am we want to have a new line and then over here we can again use print Ln and this would be the output debug and display so all types which want to be printable must implement the standard format formatting trade debug or display automatic implementations are only provided for types such as in this standard Library all others have to be manually implemented let's first look at debug the implementation of debug is very straightforward all types can derive the debug implementation this is not true for display which must be manually implemented and we have used that before this is the debug notation must be used to print out the type which has implemented the debug trade so as you can see over here we have a custom type this structure cannot be printed either with display or debug to make this struct printable with debug we can derive the automatic implementations provided by rust so again we can use a derivable trait meaning this struct over here implements the debug trade meaning we can print it out using debug notation so fill in the blanks and fix the errors as you can see we have here a custom type structure holding an i32 value types in standard and rust have implemented the debug trade so as you can see this is of type i32 now all of the types in the standard Library have implemented debug or even display so we can use here to display notation and this will print out fine now over here we must use debug notation because this is a type that we have created and again to use the debug notation we have to derive the debug trade like that and that means structure implements the debug trait and we can then print it out so this i32 type will be printed automatically because it's a type defined in this standard library and over here we just had to derived debug trade and then use debug notation here so format debug definitely makes one type printable but sacrifices some Elegance maybe we can get more elegant by replacing this notation with something else but not this alright let's see we have here a custom type that derives the debug trade then we are here instantiating it and when we now execute the program as you can see the output will be like this now we want the output to look something like that and we can for that use the pretty notation just adding this hashtag symbol between the colon and the question mark and then the output should look much more pretty as you can see the output will then be like this and notice we first had to derive the debug trade in order to print it out using debug notation we can also manually Implement debug trade for our types so as you can see we have here two custom types structure and deep now structure holds a type of i32 and depos a type of structure meaning these are basically nested so we have keep which holds a structure which holds an i32 right the problem with derive is there is no control over how the results look what if I want what if I want this to just show A7 so as you can see we are here instantiating it with a value of 7. now when we print it like that then as you can see the whole structure will get printed out but we only want tip value over here so what we can do is we implement the debug trade manually meaning we can manipulate the output so let's see the debug trade in the documentation and as you can see this is the debug trade as defined in this standard Library so let's copy this function signature so we can then over here implement the debug trade and this is found in a module M and this is inside the module fmt from the standard library and we want to implement it for the Deep over here now we remove that and we also remove that because we are implementing debug trade manually right we don't need the derived implementation so let's see deep then I will copy the function signature so here we can use a custom type fmt result and you know what we can use a namespace here so then we don't have to write here standard all the time like that I can delete this lifetime and now we can implement the debug trade namely this fmt method so again we want to write to a buffer so we will still write macro and it should write to this formatter which is provided by the debug trade and then over here all we want to actually print out is in this case self right the instance over here and then we access the first element so when we access the first element in a tuple we use the dot notation like that and inside here we have now accessed structure and we want to access this value this structure struct holes right so we again use zero and that's basically it now it should print out and of course over here fmt formatter this is also from the fmt module and as you can see now 7 will print so all we did over here is manipulating how the output of the debug trade looks like when we use debug notation and as you can see we have defined for the Deep custom type over here how the output should look like so the output will just be the inner element of deep and structure right this i32 and we just access that and write it to this buffer all right display here debug is simple and easy to use but sometimes but sometimes we want to customize the output appearance of our type this is where display really shines unlike debug there is no way to derive the implementation of the display trade we have to manually implement it another thing to note the placeholder for display is this and not that so this would be for debug trade for display we are using that and you know that already so make it work as you can see again we are using the fmt module and we have a custom type 0.2d holding two fields of type f64. now we want to implement the display and the debug trade for this custom type meaning when we instantiate this point 2D then we can pass the instance over here and it will give us this output right so we are using here the display notation and debug notation and as you can see we can here Define different outputs so when we pass an instance of point then the display trade actually will output something like that if we use debug notation then we want the output to look something like that and that's what I mean when I say we can manipulate how the output of a custom type will look like so I will take over here this method and I will just copy it over here now let's first implement the display trait as you can see we want the output to look something like that so we Define here lay the X field the value of x field and then plus the value of the by field and we have here an i okay like that and we can access these fields using normal notation self dot X and cell dot y right and for debug notation we want the output to look something like that so let's copy this put it inside here and as you can see we want here the value of the X field and here the value of the Y field and as you can see we are here using curly braces and this is usually for the placeholder so what we have to do here is using two of them meaning this calibrases over here will basically be escaped all right and then over here we have to provide the arguments self.x and self dot y let's see and this is compiling now let's see the actual output as you can see I'm using display notation and debug notation and just provided an instance of the point to the type and as you can see this will then be the output so question mark operator implementing fmt display for a structural whose elements must be handled separately is tricky the problem is each right generates a fmt result which must be handled in the same place fortunately Russ provides the question mark operator to help us eliminate some unnecessary codes for dealing with fmt result so let's see again we are using the fmt module and we have here a custom type a struct list a tuple struck list holding a vector of i32 elements right over here we are instantiating this list with a vector holding one two three now as you can see when we put this list over here into the format macro then we want the output to look something like that so we want to print these square brackets the index colon and then the actual value now let's see how this would be implemented notice over here we are implementing the display trade so we can then use it to display the type directly so as you can see this is the function signature from the fmt module and as you can see over here we return a type of fmt result now this is a third now this would be a type Alias and we have covered that now over here we want to extract the value using Tuple indexing and creating a reference to back so the first thing we do is we want to access the vector in the list struct and we do that using Tuple indexing right we want the first element because there is actually only one element on here so we access that meaning we'll get back a reference to a vector holding i32 types right here we are taking a reference now then we have basically unwrapped this Vector into this variable now as you can see we use the right macro to write to this buffer this square bracket because as you can see this is what we want to Output so the first thing added to the buffer will be this square bracket then iterate over V in vac while enumerating the iteration count in count so we take this vector and we iterate over it and enumerate meaning we will get back a tuple holding the index and the actual value then for every element except the first add a comma use the question mark operator to return on errors so as you can see if count is not equal to zero meaning if it is in fact the first element then we don't add this comma all right because we don't want to look something like that so this is only after the second iteration that this comma will be added and then we write the actual value over here and this iterates three times right for each element and then in the end it will put into the buffer this square bracket so when we run that as you can see the output would be something like that but this is the output that we want so what's missing over here is we didn't include the indexes right so what we can do over here we want here to be the index and then a colon like here and a space and then the value so we have the index in this count variable so let's use that and again we can then run the program and this succeeded so let's actually print that out as you can see this would be the output so we can really manipulate the output of our custom types using the debug and the display trades and by the way as you can see we are here using the question mark operator because this over here could potentially fail right that's why we are using this here and we use here the result type from the fmt module just in case any of these operations here fail alright see you in the next topic so let's take a look at lifetimes now lifetimes are only necessary when dealing with references and even then most of the time the compiler can actually infer the lifetimes and we don't have to worry about it so let's see a lifetime is another kind of generic ensuring that references are valid as long as needed every reference has a lifetime which is the scope for which the reference is valid most of the time this is implicit and inferred so we don't have to worry about it sometimes though lifetime annotations are needed and that's the case if the compiler can't infer it and lifetime annotations is a concept which most other programming languages don't have so even for experienced programmers this sometimes looks a little bit confusing but in actuality it's a very simple concept and you will understand it very quickly so actually the main aim of lifetimes is to prevent dangling references also called dangling pointers so let's see we are here in Main and we declare a variable R we don't initialize it with a value but just declaring it meaning the compiler will actually infer a lifetime of a for this variable and the lifetime a in this case is valid until the end of the main scope right then over here we have an inner scope and in this scope we are initializing variable x with a value of 5. so the compiler will annotate this variable to be of Lifetime B and as you can see lifetime B is only valid until the end of this scope now then we are assigning a reference to x to the variable R meaning the variable R we have declared here holds a reference to the variable X the problem here is that when X goes out of scope at this point then R Points to something that is not valid anymore right and that's the aim of references to avoid exactly this scenario now to understand lifetimes you have to understand the borrow check or in Rust so the borrow Checker Compares Scopes to determine whether all borrows are valid it's a key part of Russ ownership system it tracks lifetimes of references and ensures that they don't violate the ownership rules these rules ensure that the value is not accessed after it has been moved or freed from memory an important a reference to a value must never outlive the value itself so you have to remember especially the last line a reference to a value must never outlive the value itself so when we go back here as you can see we have here a reference to value 5. now the problem is that this reference outlives the value itself meaning X holding this value we are referencing to goes out of scope meaning the reference on here lifts longer than the actual value it is referring to all right let's see the compiler uses lifetime to ensure all borrows are valid typically a variable's lifetime begins when it is created and ends when it is destroyed so the scope of Lifetime annotate the lifetime of I and borrow 2. lifetimes are annotated below with lines denoting the creation and destruction of each variable I has the longest lifetime because its scope entirely encloses both borrow1 and borrow two the duration of borrow 1 compared to borrow 2 is irrelevant since they are disjoint so when we look at this program here what you see is that we have here initialized variable I right now this variable I over here lives until the main scope ends all right basically it outlives this scope and this scope now over here we are assigning a reference to I to borrow one right so borrow one lifetime starts and then we are just printing out borrow one now over here borrow one will go out of scope but it doesn't matter because the value itself still lives on all right and over here the same thing we have power 2 which holds a reference to I meaning that borrow 2 lives inside the scope so we print here borrow two and then it will here go out of scope but again the value here lives on so the actual value outlives T borrows or the references all right example two so over here we have a variable X with a value of 5. and then over here we have a reference to X assigned to variable r and then we are printing out R right so if we execute that it will compile because again as you can see we are declaring here the variable X in this scope so variable X we live until the scope ends and the same thing for the reference to X so the reference to X is assigned to R meaning R will live until the end of this scope so the value and the borrow to the value are both valid all right and over here you can see the lifetime annotations the important thing here is that both the value itself and the reference to the value or valid right the reference here doesn't outlive the actual value it is pointing to annotate R and X as above and explain why this code fails to compile in the lifetime aspect that's actually the example we have seen so we are here declaring r and over here in this inner scope we declare a variable X and initializing it with a value of 5. then we are assigning a reference to X to R the problem here is X will go out of scope so R still points to dislocation but this over here has gone out of scope right so rust won't allow you to compile this program and you will get a compiler Arrow saying X does not live long enough right because the reference to the value lives longer than the actual value lives right meaning the lifetime of X is shorter than the lifetime of r or the lifetime of X holding the value is shorter than the lifetime of R holding the reference to a value and this should never happen so lifetime annotating The Borrowed cycle reuses explicit lifetime annotations to determine how long a reference should be valid but for us users in most cases there is no need to annotate the lifetime because there are several illusion rules before learning these rules we need to know how to annotate lifetime manually so ignoring illusion rules and that's the next topic lifetimes in function signatures have a few constraints any reference must have an annotated lifetime any reference being returned must have the same lifetime as one of the inputs or be static so over here we have a function print one and it takes as an argument a reference to an i32 type alright now this is how we would annotate a lifetime it's actually very similar to generics but we use this apostrophe syntax and a lowercase letter by convention just going from a b c d and so on so in this case we annotate here that the reference that the reference here is of Lifetime a so when we have over here a lifetime annotation then we can provide here then we can annotate it here for our reference right and again we are just dealing with lifetimes when we are dealing with references so in this case we say the reference to the i32 type should be of Lifetime a now when we annotate that to our function it means that the lifetime a must at least live as long as the function itself or we all or we could also say lifetime a must outlive this function so the past reference over here must outlive this function this is just ensuring that the reference passed here lives longer than this function meaning we can ensure that this function always deals with a valid reference right and all we do here is printing out X mutable references are possible with lifetimes as well so if you would have a mutable reference you would annotate it like that first after the Ampersand of course you will always put the lifetime annotation and then the mute keyword and then of course the type multiple elements with different lifetimes in this case it would be fine for both to have the same lifetime a but in more complex cases different lifetimes may be required so as you can see we can Define more than one lifetime over here we have lifetime a and lifetime B meaning the references pairs to this function could be of different lifetime but again lifetime a and lifetime B must outlive this function so the past references over here must outlive the function right and returning references that have been passed in is acceptable however the correct lifetime must be returned so let's take a look at the function pass X as you can see it takes two arguments and both of them are references now this would be of Lifetime a and this would be of Lifetime B again both of these references must outlive this function and we are just returning X which is annotated with a lifetime a meaning the return type will return meaning the return type will be a reference to an i32 with a lifetime of a right this would be the type that is annotated to the X argument here if we would here have y and we return y then we would do it like that right because we are returning a reference with a lifetime annotation of B foreign so as you can see we have here two variables holding i32 types and then we are passing to all of the above functions T reference to these two variables right because all of these functions over here expect a reference to an i32 type now in this case this program will compile as you can see this is compiling because we have here declared X and Y in the main scope meaning they are valid until the end of the scope over here right so t x and y references we pass to the functions outlive all the functions which is a requirement right the best references must outlive the function so we don't have to deal with dangling references and so let's see an exercise to understand it even more make it work by adding proper lifetime annotation so over here we have the function longest that takes two arguments of type string slice now we then test the length of these string slices and if x is and if x is longer than y then we return X otherwise we return y so if we go over here in the main function and let's do some variables as you can see we have two string literals long and longer now we would pass that to this function over here now it could be possible in this case the return type is known at compile time but sometimes this is decided at runtime and when we are passing these arguments to the function over here let's do that actually and notice we don't have to pass here a reference because string literals are already references all right now if we would compile that this would actually fail right missing lifetime specifier so what we want to achieve here is first of all we want to add a lifetime parameter like that and then we want to define the lifetimes of these references now in this case the function over here could either return X or Y right depending on which argument has the longer string right so sometimes it might be X that is returned and sometimes it might be y so we can't be sure if we annotate here a and B like that we don't know which one will get returned is it a or b it depends on the arguments right so what we can Define here is that X and Y these two arguments have the same lifetime meaning these two references share the same lifetime they must be declared in the same scope and both of them of course must outlive the function longest so in this case this is what we are providing right we are declaring X and Y over here in the main scope so when we call the longest function with these two references over here it means that these two references still live on even after this function call all right because again the reference of X and Y will leave until the main scope ends now because we have here defined that both of these references have the same lifetime it is clear that the returned string slice must have the same lifetime either X or Y right let's see and as you can see this is compiling in this case longest will return y because it is longer than this than the X string all right a must live longer than the function here reference to string Foo would create a string followed by a reference then the data is dropped upon exiting the scope leaving a reference to invalid data to be returned fix the arrow in three ways as you can see we have the function invalid output we Define here a lifetime parameter a and declare that The Returned reference should be of this lifetime now the problem here is we are creating a new string and returning a reference to it right we are returning a reference to a string the problem is this string was declared inside the function now when the function returns to the caller it means the string over here will get out of scope right because it is declared inside the function so again if we call for example if we call this function then we would get back a reference to a string right the problem is that the reference points to something that isn't valid anymore this string declared here got out of scope so X points to something that is not valid anymore so returning a reference to something declared inside the function is a really bad idea now what we can do over here to fix that and this is probably the easiest fix we just return the string itself right so then ownership will be transferred and X will be the owner all right let's print out X as you can see this is working because we are here declaring a string and then return it meaning the ownership of the string will then be returned over here right so X will become the owner of the returns string from the invalid function the second way to fix that would be returning a string literal right and a string literal actually has always a static lifetime we will come back to that but that actually means that this reference is of static lifetime meaning it is valid throughout the entire program all right because remember a string literal is hard coded into the binary so it lifts as long as the program lives so then over here we will get back a string slice right let's see and as you can see this is also compiling the Third Way would be over here create a string and then we can pass this string to the invalid output so in this case we want here to take a reference to a string right we can also write it like that because remember a reference to a string will be automatically inferred to a string size right so so we are then passing a reference to this string right and what we want to return here is s meaning we return here a string slice and we can annotate a lifetime parameter and then we will print out X let's see and this is also working because as you can see we have defined here a string meaning s over here is the owner of this train then we are passing a reference of s to this function right we annotate here a lifetime a meaning the reference over here passed to the argument s must outlive this function which it does right we have declared s here so even after this function call S still lives on right and then all we do is we again return as the past reference right so it will actually have the same lifetime as the past argument all right meaning we have here then a returned string slice all right Point refs takes two references to i32 which have different lifetimes A and B these two lifetimes must live at least as long as the function print refs so as you can see we are here in the print refs function declaring two lifetime parameters one for argument X and one for argument y again we are dealing with references meaning we care about the lifetimes if these would be owned types like so then we don't deal with references right and then we just print out X and Y to provided arguments make it work if function which takes no arguments but has a lifetime parameter a so over here we can see that we annotate a lifetime parameter a and we have here and i32 integer now error X does not live long enough so we assign to Y over here in reference to X the problem again is that when we annotate this lifetime parameter it says the lifetime a must outlive this function which it doesn't right because the reference over here because the value we are referencing to is declared inside the function meaning it will then be dropped at the end of the scope attempting to use the lifetime a as an explicit type annotation inside the function will fail because the lifetime of reference X is shorter than a a short lifetime cannot be coerced into a longer one so let's see in main we are here destructuring a tuple into the variables 4 and 9 right so 4 will hold 4 and 9 will hold 9. borrows of both variables are passed into the function so we are then calling the print refs function with references to 4 and 9 right this function over here any input which is borrowed must outlive the borrower in other words the lifetime of 4 and 9 must be longer than D then that of print refs so again when we annotate these lifetimes on a function both of the lifetimes must outlive the function or both of the references passed here must outlive the lifetime of this function which they do right so 4 and 9 are declared in main scope meaning they outlive this function over here so this should actually work so failed borrow contains no references to force a to be longer than the lifetime of the function but a is longer because the lifetime is never constrained it defaults to static so so as you can see we have here declared the lifetime a which states that the reference holding the lifetime of a must outlive the function this is not the case right because we are declaring the value inside the function itself now when we remove that actually this over here will then work all right let's see let's see structs make it work by adding proper lifetime annotation a type of road which houses a reference to an i32 the reference to i-32 must outlive borrowed so as you can see we have here a custom type a tuple struct holding a reference to i32 now in this case we have to annotate the lifetime and we do it like that and then we pass it to the reference like so and that means this reference here must outlive this struct or the instance we create from it right similarly both references here must outlive this structure again we are here annotating the lifetime and we say that both of them should have the same lifetime which makes it easier right and enum which is either an i32 or a reference to one so as you can see again we can annotate a lifetime basically like a generic type parameter and over here as you can see this variant holds a type of i32 so the enum will be the owner of this type we don't care about any lifetime annotation we only care about lifetime annotations when dealing with a reference like that now let's see we have here i32 and i32 and then we are instantiating the borrowed struct with a reference X right so as you can see this would be the borrowed struct and it holds a reference 2x which is i32 right now as you can see we Define here that reference of Lifetime a must outlive this struct or the instance of destruct right so X over here must outlive this instance which which it does right because it again it is declared in main scope meaning X and Y we live until this point here so they outlived The Borrowed struct and over here as you can see we pass here references to the X and Y field of the named borrowed a struct so this would then be an instance now again X and Y in this case first of all they are declared in the same scope so they can have the same Lifetime right they both live in the same scope and both of these variables will end at the same time so this will actually compile right everything over here is safe so as you can see we instantiate clear the either enum and one time with the variant ref holding a reference to X now again as you can see we have here a lifetime parameter a which states it must outlive the instance of the enum which it does right X is valid all the time and the second variant doesn't hold a reference we just pass it to single we just pass it T variable now because this implements copy it's an i32 it means that even then y will still live on so either want to take ownership it will take ownership of the copied value let's see if this is actually compiling and this is compiling make it work so over here we have destruct no copy type and and over here we have struct example and as you can see we annotate here the lifetimes A and B meaning over here the A and B field could hold references that have different lifetimes but they can also be the same all right now let's see in main a tight to FN main stack frame so as you can see we have here an i32 and over here we have declared a variable which should be of type example but we haven't initialized it with values all right we haven't instantiated the example struct now notice here we have another scope lifetime B tied to new stack frame so then we have here an instance of the no Copy Type struct and as you can see fix me so then we are instantiating example with the fields over here passing it concrete values as you can see we are passing it a reference to Bar a this and a reference to Bar B so the problem over here is that VAR B will go out of scope at this point right meaning the example struct we have instantiated over here will hold the B field which points to something that is not valid anymore as you can see we are here printing out example now if we would print it out in this scope then this would actually compile and of course this must be your 32 because it is a u32 field here as you can see this is compiling fine now even though we are here in a new scope right we have declared Bar B here in the inner scope but even then both of these references are valid right because we are here then using example in the same scope and we have noted that here that you that the lifetime of a and b are different right so the lifetime of bar a is actually longer than the lifetime of Bar B right because for a lives until the end of the main function while Bar B leaves only until this point but again this is safe and this is compiling because the VAR B we have created here and passed a reference to the example struct is valid if we leave this over here then this wouldn't compile right because vorby gets out of scope and we over here say that both A and B must outlive example all right so example is used here but not all references are still valid which would lead to a compiled error but what we can do also is just removing this scope here meaning everything is now declared in main scope so this would also compile right because all the references to these variables are valid let's see here we have again struck no copy type and the example struct we have seen before and as you can see denoting that the lifetimes of these references might differ but they can be the same it's no problem so let's see here fixed function signature we have here the fix me function which takes us an argument a reference to an example instance and it will then return the B field from the provided argument from the example struct right it will return this field over here meaning a reference to no copy type because that is what B is holding now over here we are instantiating no Copy Type and we are instantiating the example struct so we pass for the field a here a reference to one and for B we pass a reference to no Copy Type right and then we pass a reference this example instance over here to the fix me function now let's first annotate the lifetime parameter again we do a and over here we say that the past reference to the example instance must outlive this function right which it actually does because here we are instantiating the example struct and passing it to a reference and passing it as a reference to this function meaning example over here will live longer than the function right and we can then Define that no Copy Type must also outlive this fix me function all right which it does because again it's secured the main meaning the end of scope will be at this point so both the instance of example and the no copy type both outlive this fix me function and as you can see we can pass this same lifetime parameter because they are declared in the same scope meaning they have the same lifetime methods are annotated similarly to functions so we have here destruct owner holding an i32 type so annotate lifetimes as in a standalone function as you can see we have here the implementation block for owner and we Implement some methods for this custom type and this is how we would annotate lifetime parameters again same thing again same thing like in functions make it work by adding proper lifetime annotations you have a struct important excerpt and the port field holding a string slice now again string slice is a reference meaning we have here to annotate the lifetime parameter so that means the reference that the port field of destruct is holding outlives the entire struct right and over here we have a method implemented for the important excerpt struct so as you can see we are using a lifetime parameter here but we have to actually first declare it like that and that's basically it it should compile so when distract over here declares a lifetime parameter we have to declare it in the implementation block again but we are here not using it meaning we can do it like that right we don't care about this lifetime parameter here and this lifetime parameter is declared only on the on the method right this is compiling but there is an easier way actually we don't have to use any lifetime here right because we can Define here that the string slice is static meaning it will live throughout the entire program all right illusion some lifetime patterns are so common that the borrowed Checker will allow you to Omit them to Safe typing and to improve readability this is known as illusion illusion exists in Rust only because these patterns are common for a more comprehensive understanding of a lesion please see lifetime illusion in the official book so I have prepared some slides let's see there are three rules of Lifetime illusion the first rule states compiler assigns a lifetime parameter to each parameter that's a reference second if there is exactly one input lifetime parameter that lifetime is assigned to all output lifetime parameters third rule if there are multiple lifetime parameters but one of them is a reference to self or immutable reference to self the lifetime of self is assigned to all output lifetime parameters so let's see an example and before we see the example as you can see lifetime illusion makes writing rust much more easy until this point we didn't worry and care about lifetimes and that's most of the time the compiler just inferred it so we didn't have to worry now let's see here we have the function first word taking a reference okay and returning a reference so the compiler applies the first rule each parameter gets its own lifetime so the compiler behind the scenes will annotate here a lifetime for this function and we'll annotate it for all the references right so the second rule applies because there is exactly one input lifetime again we have only one argument that is a reference so this gets assigned to the output lifetime meaning the output reference here must then be the same reference right we are taking a reference so it must be the same when we are when we are returning a reference so in this case the compiler could infer the lifetime and we don't have to specify them manually so what the compiler will do he will just annotate the same lifetime as for the input argument right so this is all inferred you can write it like that and it will compile you don't have to worry about lifetimes so let's see this example we have the function longest that takes two references as arguments okay and it returns one reference first rule each parameter gets its own lifetime so the compiler will come and annotate A and B each reference gets its own lifetime all right so here the second rule doesn't apply because there is more than one input lifetime also the third rule doesn't apply because this function is not a method we have now to manually annotate the lifetime parameters so as you can see over here the compiler cannot infer The Returned reference here is it of Lifetime a or is it of Lifetime B right so it depends if you return X then you would annotate a lifetime of a if you return B then you would return then you would annotate a lifetime of B and again both lifet times A and B must outlive this function let's see an example in a method so as you can see we have a method taking a reference to self meaning taking the instance as a reference and doesn't take ownership so here the third rule applies which states that if there is a reference to self then all references will have the same lifetime as self right so we can have here any number of arguments all of them will take the exact same lifetime as the lifetime of self because this is inferred and it makes sense right because all of the provided arguments to a method must actually outlive the structure or the enum instance all right so all of the arguments here take implicitly the same lifetime as self all right so let's see remove all the lifetimes that can be elided as you can see over here we have only one input argument and the second thing is we don't even return anything so we don't even have to care about uh the lifetime because we don't worry that it might go out of scope right but in this case we only have one argument meaning the compiler is able to infer that over here again we have only one argument but as you can see the difference is we are returning a reference right so because there is only one argument here the compiler can infer that the reference over here must be the reference that will be returned so we don't have to annotate the lifetimes so let's see over here we have the longest function and the first rule says that the compiler will annotate a lifetime for each reference meaning it will create a and b because we have here two references right two arguments and it will annotate for the first argument a and for the second argument lifetime of B now over here this the compiler cannot infer which lifetime will get returned meaning as you can see we are returning X so the lifetime over here would be a right the lifetime of the argument X but notice both of the references must outlive the function right here we have a struct owner that holds an i32 type annotate lifetimes as in a standalone function so as you can see we have here methods implemented on the owner struct now again when we have a mutable reference or an immutable reference to self then we never have to worry about lifetime annotations because this will get inferred by the compiler all right all references pass to these methods will get the same lifetime as self or SD reference to self now over here we have to annotate the lifetime because this is a struct and we want to ensure that the reference the name field is holding is actually living longer than the instance of a struct person okay and over here the same thing let's see if this is compiling and this is compiling what we could do also here we could remove that and doing it like this all right this should actually also work okay we have here to specify that this string is static in this case we have to explicitly state it and as you can see this is compiling so you don't have to declare here a lifetime parameter you can put here this static Lifetime and static lifetimes will be our next topic let's take a look at static lifetimes so aesthetic lifetime refers to a lifetime that lasts for the entire duration of the program's execution any reference or borrowed value with a static lifetime can be safely used throughout the program and aesthetic lifetime can be coerced to a shorter lifetime if needed so we have seen string literals and they are usually annotated using an ampersand and stir but implicitly it would have a static lifetime so string literals have a static lifetime because they are hard coded into the executable meaning they are valid throughout the entire duration of the program's execution let's see static is a reserved lifetime name you might have encountered it several times so a reference with static lifetime is for example a string literal and this would be the full type annotation static as part of a trade bound so as you can see we are here defining a generic type parameter and we say that the argument provided should be of type T now in the Vera Clause over here we see that t must be of static lifetime though they are all static but subtly different as a reference lifetime Ampersand static indicates the data pointed to by the reference lives as long as the running program but it can still be close to a shorter lifetime there are several ways to make a variable with static lifetime two of them are stored in the read-only memory of the binary fill in the blank in two ways so as you can see we are calling here neat static within argument of V so let's declare that here and as you can see the argument must be of type static string or string literal all right and then we are here checking is the argument equal hello so let's initialize it here so the first way is just initializing it like that without any type annotation because this is simply inferred by the compiler so any string literal is hard coded into the binary means it lives as long as the program's execution Because the actual data of the string literal is living in the binary the second way is just annotating the full type annotation like that and this static lifetime annotation here can also be omitted so we can annotate it like this as you can see the compiler is able to infer the lifetime another way to make static lifetime is using box leak now this is an unsafe method and I won't cover that because it's a more advanced topic but I have solved it and will put it on my GitHub if you want to solve it for yourself static only indicates that the data can live forever not the reference the letter one the letter one will be constrained by its scope so let's see make a string literal and print it so over here we have a string literal meaning it's a string slice of static lifetime then we print it out when static string goes out of scope the reference can no longer be used but the data remains in the binary so at the end of this scope over here this variable will go out of scope meaning it's dropped and that means we can't then use static string anymore because the variable has been dropped now even though the variable now even though the variable has been dropped the data over here is actually still living on right so this is living in the binary a string literal is hard coded into the binary meaning it is of static lifetime meaning it is valid throughout the entire Pro programs lifetime but of course a variable is bound to its scope so to make that work we can take that and copy it over here meaning the variable static string is valid until main ends as you can see static can be close to a shorter lifetime so over here make a constants with static lifetime so this over here is the static keyword meaning we are here creating a constant now constants by conventions are always uppercase right and by the way the difference between conist the difference between this and this now both of them live for the entire lifetime of the program both are of static lifetime but the difference is that static will always remain at the same memory location while a const constant will get inlined so when we use for example num const here then it will get inlined meaning the compiler will basically copy that into the code in the function so the memory location could change but static always stays at the same memory location so returns a reference to num there is static lifetime is coerced to that of the input argument so as you can see over here we have a function corostatic and we have defined a lifetime parameter a we Define here that the input reference should be of this lifetime a right meaning the reference we take as an argument to this function must outlive the function itself now we over here actually return this constant right basically a reference to this constant and we can in this case course the static lifetime of this num constant to a shorter lifetime it's no problem right so a is shorter than static right aesthetic lifetime is larger than this a lifetime here meaning aesthetic meaning a reference with a static lifetime lives longer than a because a only defines that it must outlive this function right but we can hear chorus it to a shorter lifetime so it matches with the lifetime annotated on the function make an integer to use for coreostatic so over here we make an integer and then we pass a reference of this integer and called the chorus static function meaning we here returned a reference to an i32 right basically a reference to the number 18. and then we will just print out the result of this function call right now as you can see we have crossed num here to a shorter lifetime but this doesn't change the actual lifetime of this static constant right so even after this scope over here we can still access num because again it's of static lifetime it lives as long as the program is running so even though we have here corrected to a shorter lifetime it doesn't matter it remains static right let's see as a trade bound it means the type does not contain any non-static references for example the receiver can hold on to the type for as long as they want and it will never become invalid until they drop it it's important to understand this means that any owned data always passes a static lifetime bound but the reference to that owned data generally does not so so let's look at function printed as you can see we Define here a generic type parameter meaning the input meaning the input argument here must be of type T now this over here is a trade bound meaning we say the type T must implement the debug trade and T cannot contain any reference that is not of static lifetimes so all references T is containing must be of static lifetime right and over here we do basically the exact same thing but using the impul keyword right this and this is actually quite the same and then over here as you can see 22 takes a reference to T right and again T must implement the debug trade and it cannot contain any non-static references all right so I is owned and contains no references thus it's static as you can see we are here initializing I with a value of 5 meaning I is the owner of 5. now when we pass the owner to a function then it would actually comply with this function signature because owned types are also considered static okay now this would actually work because again i32 implements debug and it is considered static because we are passing an owned type right oops reference to I only has the lifetime defined by the scope of main so it's not static right so the so a reference to I has a lifetime that is defined by this main scope so it's not static anymore right so this would actually output and error right and this would be the same thing again print it and print it one would actually be very similar right now this here actually works because we are here passing a reference to I again right so we pass a reference to I and again the reference to I would have a lifetime of this main function now the difference is here that in the argument we defined that the argument should be a reference to T meaning if we think away it is ampersands I would be T right so a reference to T would be a reference to I hope this makes sense so that means again when we pass I directly it would be it would Implement debug and it would be of static Lifetime right so this over here would work now to fix this over here we can do it two ways we can create here the constant I and again constants have a static lifetime as you can see this is compiling and the second thing is we can use this static keyword meaning I is static so this would pass because the argument we are passing or the references we are passing are all of static Lifetime and they implement the debug trade right i32 implements debug and we have ensured that all of them are of static lifetime now the last one here again deals with box leak I won't cover this but again the solution is in my GitHub if you want to solve it you can do that and then compare with my Solutions alright see you in the next one so let's see closures a closure is an anonymous function that is able to capture the values from the scope in which it is defined and if you are coming from python then you might know Lambda functions and in JavaScript they are called Arrow functions and they can be defined inline for example as a function parameter they don't require type annotations and they can take ownership of a value by using the move keyword so all functions and closures implement the F and trades and that is the trait that defines the signature for closures and functions it describes the types the number of arguments and the return type there are three different traits FN ones the closure can be called only once because it takes ownership of the captured values FM mute it might mutate a captured value it can be called more than once and FN it doesn't take any ownership of captured values it doesn't mutate anything and it might not even capture anything from its environment so let's take a look over here we have here initialize the variable x with a value of 1. now over here we would have a closure and closures are defined like that first of all we have these symbols over here and between them we Define the name of the argument and as you can see over here we just add the argument with the value of x so as you can see the closure here is able to capture the value of x something a function can't do right and we then assign this closure to a variable meaning we then can call this closure and calling it like in normal function so in this case as you can see we call the closure using the variable name closure and then providing it an argument right so actually disclosure call should evaluate to 3 because we provide 2 here meaning weld will be 2 and adding X to it one so two plus one will be 3. so disclosure captures the value of x and modifies it the compiler will capture variables in the least restrictive manner possible in this case immutable reference of X is taken rather than taking ownership because it's less restrictive let's see closures can capture the enclosing environments for example we can capture the X variable that's the example we have seen from the syntax we can see that closures are very convenient for on the Fly usage unlike functions both the input and return types of a closure can be inferred by the compiler so we have here a normal function as you can see it takes an argument of type i32 and it will just add 1 to T and it will just add 1 to the provided argument and returns it meaning the return type will be i32 now closures are Anonymous here we are binding them to references these nameless functions are assigned to appropriately named variables so as you can see we can here create a closure and this and this over here are exactly the same so type annotations enclosures are not required okay and then again we assign the closure to a variable in order so we can call it all right over here we have a variable I with a value of 1. and then we are just calling the function and these two closures with this value over here and a closure taking no arguments which returns an i32 the return type is in third so this would be a very primitive closure just with running one when being called so when we call one here we will just get back one let's execute that you can see when we call this function and these two closures it will just add one to the provided argument and over here we are just returning one capturing closures can capture variables by borrowing or moving but they prefer to capture by borrowing and only go lower when required so they can capture the values either by reference by mutable reference or by value meaning the closure will then only type it captures okay so let's see exercise one make it work with least amount of changes so over here we are initializing a variable color with a string so as you can see over here we have a closure and we assign it to print so we can then call it now this closure here doesn't expect any arguments but it uses the move keyword meaning the captured values will be moved inside this closure right so that means the closure here will become the owner of the captured value this is not always the case so for example over here a immutable reference is sufficient because we are just reading basically this data here we don't mutate anything and we don't need ownership to print something out but as you can see printing two time over here will cause an error right so what we can do is we can remove the move keyword and color can be borrowed immutably again because the closure only holds an immutable reference to color right that means when we don't move the captured value we can then take a reference to color again so this foothold a reference to a string as you can see we print here out two times color green because we are calling this closure two times and this will take an immutable reference of this captured value all right and then we can take another reference of this string so let's see exercise 2 make it work don't use reborrow and count reborrowed don't modify assert EQ so we have over here an i32 by the way this is mutable and we have here a closure taking no arguments then as you can see it captures this variable here all right and it mutates it meaning count will then hold one and then we just print out count over here we are calling disclosure and then we are taking a reference to count meaning a reference to an i32 right and then we are again calling this ink closure the closure no longer needs to borrow mutable borrow account therefore it is possible to re-borrow without an error so what is happening over here we have seen that the compiler will take the least restrictive approach so in this case it will take an so in this case the count variable here will be captured as a mutable reference right and that would be a problem because as you can see we are calling Inc and over here we call it again after you're defining an immutable reference to count so maybe you remember that you can either have one mutable reference or any number of immutable references but not both at the same time so this would be a problem so we can call here the move keyword meaning disclosure here takes ownership of this value now again because i32 implements the copy trade it means it will actually get a copy of count so count is still accessible even after this closure call right the closure will just take a copy of the value and that means we can then take an immutable reference again to count and call the closure right and over here the closure no longer needs to borrow a mutual reference to count therefore it is possible to re-borrow without an error so as you can see we are here taking a mutable reference to count again because we don't use this immutable borrow over here again after this point right so this is allowed let's see and as you can see this is compiling and here you can see that count has been modified so let's see exercise 3 make it work in two ways none of them is to remove take movable away from the code so as you can see we have here initialized a variable with a heap allocated i32 integer right then over here we have a closure and we capture here this movable variable now we have seen that actually the compiler will use the least restrictive approach so in this case because we have only a print line it will take this variable here as immutable reference we just need to print it out now over here we are calling the take function as you can see the take function takes an argument of any type but it takes ownership meaning when we call the take function with an argument the argument will then lose its ownership so take function will become the owner of movable in this case right and that means we can't call disclosure two times because in this case again the compiler will try to use the least restrictive approach but in this case it's necessary that this closure over here will Implement F and once so it only can be called one time because again over here move a bell when we call this take function it will take movable and take ownership of it because it's necessary for the compiler to do that to comply with this function signature but what we can do is we say we want a reference right so again the compiler will then implement the F and trade for this for disclosure meaning it can be called two times now we will see a lot of more example of these FN trades so don't worry it just means now because we just need over here immutable references the compiler will actually capture this variable here as an immutable reference right so nothing gets moved inside this closure and movable will remain the owner of the data for comparison the following code has no error foreign so as you can see we have again a heap allocated integer and we have here a closure now as you can see we use the move keyword here and in this case disclosure will capture the movable variable only as an immutable reference because we here only print out the variable we don't need an we don't need a mutual reference and we don't need to take ownership so we can call this closure two times without any problem type in third the following foreclosures has no difference in input and return types so this is actually a function not a closure but as you can see all of them are exactly the same so we can skip the type annotations and we can even skip the curly braces if it is declared on the same line so this over here is exactly the same as writing it like that so over here we have an example closure taking one argument and just returning that argument so then V over here call the example closure with a string meaning over here this would be the argument provided and it returns a string now when you define a closure and call it with a concrete value the compiler will then infer the types of the arguments and the return type so in this case the compiler will annotate the types like that right we take an argument of type string and return the string and that means that we then can't call the closure again with another type this would cause an error okay what we have to do is to convert that to a string let's do it like that so we get back a string here and this is compiling so let's see FN F and mute FN once when taking a closure as an input parameter the closures complete type must be annotated using one of the following trades FN the closure uses the captured value by reference F and mute the closure uses the captured value by mutable reference F and once the closure uses the captured value by value basically taking ownership alright so make it work by changing the trade Bound in two ways so when a function accepts a closure as an argument we have to actually Define what will be the type of the arguments to the closure and the return type of the closure right and we over here can see that a closure implements one of these three trades all right now over here in the fn1's function we Define that the fund argument must be of type f and here we Define the trade bound so F must be a type that implements F and ones taking as argument U size and returning a Boolean value all right and inside this function over here we are just calling the provided func closure and providing its arguments so as you can see we have here a vector and we call the FN ones function with an argument of a closure as you can see we can't pass a closure as a function argument now in this case disclosure will implement the fn1 straight right basically meaning it will take ownership of the captured values and it can be only called once so it takes as argument a a type of view size right so and this is necessary here because we are comparing the provided argument 3 and 4 with the return value of the Len method right this land method is defined on vectors and it will return EU size so that over here has to be of hue size we have to Define that and all this does is we can provide disclosure an argument and it will check is for example 3 over here is it the same as the length of this vector now the problem here is that we have to change the trade bound because at the moment we are calling the funk closure passed as argument to this function two times while it implements FN once meaning it can only be called once now what we can do over here is we take FN right basically just taking an immutable reference of a captured value now the captured value in this closure here would be X right this vector acoustic because the closure captures it from the environment it is defined in and this should actually work let's see as you can see when we pass this closure to this function and we Define here the closure must implement the FN trade taking a u sizes argument and returning a bull then we are calling the closure right Funk here holds a closure so we are calling it with three in this case this would evaluate to true because the length of vac is three elements right so x dot length will return three meaning it's equal to the provided argument and four of course would return false so we are here initializing a string and over here we are defining a closure it takes an argument and it then modifies the string over here basically pushing the string that was provided in the argument then we are calling the exact function providing it this closure all right then let's see all this function does it takes a closure as an argument and then it will call the closure and it will pass it a string literal right meaning that to this string as this string literal will be pushed right so after calling this closure s should then hold hello all right and then we just print out s now what would be the appropriate trade Bound for this closure think about it we have FN FN mute and FN once so what would be the least restrictive approach it would be FN mute right because we are mutating here this string the captured value but we don't need to take ownership in order to be able to mutate it right now we have to provide the argument this would be of type string slice right we are passing here at this string literal which is string slice and then we have to provide a return type so actually this won't return anything right so we don't here have to provide a return type so let's see if this works and as you can see we have here a lifetime parameter and this is just ensuring that the past string literal here is actually outliving this function exact right which is the case because string literals remember are static lifetime they live the entire duration of the program and as you can see when we print out s it has successfully been modified so it's sufficient to use here F and mute but if we want we can even take ownership but then we have the Restriction that we can only call this closure one time right so if we try that as you can see this is working two which trade does the compiler prefer to use FN the closer uses the captured value by reference FM mute the closure uses the captured value by mutable reference fn1 stick closure uses the captured value by value on a variable by variable basis the compiler will capture variables in the least restrictive manner possible for instance consider a parameter annotated as fn1s to specify that the equation May capture T by reference by mutable reference or taking ownership but the compiler will ultimately choose based on how the captured values based on how the captured variables are used in the closure which trait to use is determined by what the closure does with captured value this is because if a move is possible then any of borrows should also be possible note that the reverse is not true if the parameter is annotated as FN then capturing variables by Mutual reference or by T is not allowed let's see number seven fill in the blank a function which takes a closure as an argument and calls it f denotes that f is a generic type parameter so as you can see we have here the function apply and it takes as an argument a closure and all it does it will then call disclosure right and we have here to define the trade Bound for the provided closure a function which takes a closure and returns an i32 so over here we have applied to three again taking a closure and we have 10 to Define here the trade bound the closure takes in i32 and returns an i32 and again this function over here will just call the closure and the difference is it will then return The Returned value from the closure so as you can see over here it just calls the closure but it doesn't return anything because here this is a statement it's ending with semicolon here we omit the semicolon meaning the return value of the closure will be the return value the function returns all right so over here we have a string knittering in non-copy type to own creates owned data from borrowed one so as you can see we take a string literal and call the two owned method now the UN now the two ohms method will convert a string slice to a string capture two variables creating by reference and Farwell by value so over here we have a closure then greetings by reference requires FN so as you can see this closure captures the greeting variable now all it does it will print it out meaning to take a an immutable reference would be sufficient so the compiler will implement the FN trade for disclosure because it is sufficient to only take an immutable reference to the captured variable all right mutation forces farewell to be captured by mutable reference now requires FN mute so as you can see we are taking Farwell and we are modifying it now this requires far well over here to to capture as immutable reference again you can see that the compiler tries always to use the list restrictive approach so then the compiler will instead implement the FN mute trade for disclosure all right because again we capture here a variable which needs to be mutated then manually calling drop forces for well to be captured by value now requires FN once so this mem drop here allows that we manually dropped a value from memory right but this actually requires that we take farewell as a value right so the closure should take ownership of this variable meaning the compiler will now implement the fn1 straight for this closure right so as you can see step by step here an immutable reference was sufficient so it just implements the FN trade here a string got mutated so it takes the captured values as f as IMM mutable reference meaning F and mute is implemented and here when a function needs to take ownership then of course the captured value must be captured as owned type meaning fn1s is implemented so that means if this closure implements the fn1 straight it can only be called once because you have inside values that capture the environment values by taking ownership Quality Function which applies to closure so we call the apply function here providing it an argument diary disclosure here right we are calling apply and providing diary now let's see this would be this function and as we've seen because Farwell here gets captured by by value meaning the closure takes ownership we have to Define here that it will be a closure that implements the FN once trait right now as you can see the closure doesn't take any argument and it doesn't return anything so we would annotate it like that so again this just defines that the generic type f must implement the FN once trade and over here double satisfies apply to three straight bound so here we have a closure taking one argument and it Returns the result of this operation right so we call the apply to three function providing it as argument disclosure and as you can see over here apply to 3 is a closure generic type f and V here Define that the type f must implement the FN trade and as you can see it takes as an argument an i32 and the return value and the return type will be i32 so let's see I take disclosure here and disclosure was passed as an argument to this function and all this function does is it will call the closure with an argument of 3. now X here will be replaced by three and the return will be 2 times 3 which would be 6 right so disclosure then returns an i32 namely 6 and as you can see because we omit here this semicolon it means that the return value of this closure will be the return value of this function so when we call this function over here the return value over here will be 6 as you can see this is all compiling move closures May still Implement FN or FN mute even though they capture variables by move this is because the trades implemented by closure type are determined by what the closure does with the captured value not how it captures them the move keyword only specifies the letter so the move keyword only specifies how the captured values are captured while the FN trades determine what the closure does with the captured values so as you can see we have here a string and we have here a closure with the move keyword meaning values will get moved inside disclosure now because over here as you can see as in this case doesn't need to be taken as value so the closure doesn't need to take ownership of the data because it will just print it out right so an immutable reference would be sufficient and that's exactly what this says so over here as you can see we are calling the exec function with this closure and when we see over here the exact function takes us an argument a closure that implements the FN ones trade all right so it will in fact take ownership of the captured value and then in the function itself it will just call the closure and Returns the return type of the closure now over here as you can see again we have a string and we have a closure even though we have the move keyword as you can see we Define here that the provided closure to exec only has to implement TF and trade so the string s here will get moved so basically this closure here takes ownership but we can still Define disclosure over here to just Implement FN because again even though we are taking ownership we are just printing out right the difference between move and the event rates is the event rates Define what the closure does with the captured values and move defines how it captures them so it would capture here by taking ownership but the FN trait only defines what are we doing with the captured value in this case you're just printing it out meaning an immutable reference would be sufficient so we can even here pass FN and it still would work fill in a blank so over here we have a string and this would be a closure that takes an argument and returns a string now what we do over here is pushing to this string the provided argument right and then what we do is we will return s meaning the string right so when we call over here this function and provided the closure then we have to implement the FN once rate and it will take as an argument a string literally let's annotate that with the lifetime parameter a and it will return a string right so this would be so this would be the trade bound we are defining here now why did I Implement here FN once or why did I Define that F over here the type of the argument must implement the fn1 straight because as you can see we are capturing s and modify it now over here it would be sufficient to use an FN mute right now again it would be sufficient to just use a mutable reference to S but again we are here returning as right we are returning this captured variable meaning we must be the owner to return something so we have to Define that F should implement the fn1 straight right because we are here returning s meaning the closure must be the owner in order to return it let's see and this is compiling input functions since closure can be used as arguments you might wonder can we use functions as arguments too and indeed we can so as you can see we have here the call me function Implement call me to make it work and over here we have a normal function just printing out something and in main here we have a closure right also just printing out something then we call the call me function here two times one time with a closure and one time with a function and that means let's see first of all we Define we want here A type f which is generic okay so we have to Define here a generic type parameter and then of course we have to define a trade bound right so in this case over here we are not mutating anything and we don't need to take ownership and in fact over here we don't even capture any variables right it will just print out something and it doesn't capture anything so in this case FN so in this case the F and rate is sufficient right and by the way they don't take any argument and they don't return anything so let's see and this will be the output so we call call me providing it to closure the closure implements CFN trade and then call me we'll just call the provided function and closure right meaning when they are called they will just print out this over here course closure first because closure has been called first let's see exercise 10 closure as return types returning a closure is much harder than you may have thought of now it's actually not that hard but you will see fill in the blank using two approaches and fix the error so as you can see we have here the create FN function and we have to Define here the return type so let's first see we have over here a variable that is declared inside the function meaning num will only live as long as this function lives right and then over here we are returning a closure notice we omit here semicolon meaning this whole closure will actually be returned by the function and when we go into main as you can see we call the create FN function then this function should return a closure meaning F and plane will hold a closure basically F and claim will look something like that right holding a closure meaning we then can call the FN plane closure with a integer all right because we have to Define here that X should be of type i32 because we here capture num which is of type i32 remember when we perform an operation both values must be of the same type now let's see we call the create FN function then we initialize the num variable and then we have here the closure that will get returned meaning num here will capture this variable and when we then call the closure over here that will get returned by create FN then we provided a value of 1 meaning X will be 1 and num will be 5. right meaning the value the output should be 16. when calling let's write it here after calling the closure itself the output should be 6. now let's see first of all we have to think about how is this num variable captured now again the compiler will use the list restrictive approach so it will just take a reference problem here is that we actually return this closure right so num actually must outlive this function otherwise when we return the closure and num gets out of scope then over here we hold a closure that refers to something that got out of scope so let's use the move keyword here meaning num gets moved into the closure so the closure will become the owner of this variable all right now again here we can use static dispatch or dynamic dispatch now I will go first with static dispatch so in this case we want to return a type that implements the FN trait right because over here we have the closure that we want to return so let's Implement that disclosure should implement the FN trait and again over here FN is sufficient because even though we have captured the num by ownership the actual closure only performs an operation so it doesn't modify num and remember move defines how we capture the closure and the FN trades Define what the closure will do with the captured value alright so FN here is sufficient and disclosure takes as an argument in i32 right because the argument and the num variable must match because we are performing an operation on them and the return type should also be i32 all right the result of this operation so let's see and this is compiling now let's actually see the output as you can see the output will be 6. now this is using static dispatch now we can also use Dynamic dispatch remember when you when dealing with trade bounds like this we then have to basically box this over here and we put this over here into a box right this would then be a trade object we are using Dynamic dispatch here so let's see and this is also working so we have implemented the return type in two approaches fill in the blank and fix the error so as you can see we have the function Factory taking one argument of type i32 over here we have a variable of type i32 and if x the provided argument is bigger than 1 then this will be returned and else if it is smaller than one disclosure will be returned so using here the static dispatch approach doesn't work okay we cannot do something like that because over here we don't know exactly which closure gets returned and as you've noticed they are identical right but in fact disclosure is different than this because these have been both declared and they are located in different memory locations all right so even though they are exactly the same the compiler can't know at compile time which of these closures will get returned because they live at different memory locations now what we can do over here is using the dynamic dispatch approach so for that again we are boxing these closures and we Define here that the return type will be a box that holds a trait object namely a trade object that implements the FN trait taking as argument and i32 this x here and returning an i32 right let's see right and we have to provide here the main function because otherwise the program won't compiled and this is compiling alright see you in the next topic so iterators will be the last topic we will cover together so let's Dive In iterator allows to perform a task on a sequence of items in turn iterators are lazy meaning they have no effect until methods are called that consume the iterator to use it up all iterators implement the iterator trade which provides the next method which gets called automatically when traversing over some data and some methods consume the iterator While others produce a new iterator from the provided iterator let's see the iterator pattern allows us to perform some tasks on a sequence of items in turn an iterator is responsible for the logic of iterating over each item and determining when the sequence has finished so over here we have a vector and then we are iterating over the elements in this vector and as you can see the variable X will hold in each iteration one element of the vector so when we print that out as you can see in each iteration it will just print out X which means X in the first iteration is 1 then 2 and then 3. in the code buff you may consider for as a simple Loop but actually it is iterating over an iterator by default the for Loop will apply the into ether to the collection and change it into an iterator as a result the following code is equivalent to the previous one so this is happening implicitly that V over here this vector or really any collection will be called with this into either method meaning this collection will be converted to an iterator right and then we Traverse over each item so let's see exercise one refactoring the following code using iterators so we have here an array holding 10 Elements which are all zero something like that okay 10 times then we have here a for loop iterating from 0 to array length where the actual length is excluded meaning from 0 to 9 right 10 times and then we just index into this array and print out the and print out the element living at the specific index so when we execute that as you can see we print out 10 times 0. now again we don't have to use this syntax we can just pass here the array itself and it will do exactly the same and we have seen that the compiler implicitly will put that into an iterator like that and as you can see this would be the exactly same thing one of the easiest ways to create an iterator is to use the range notation so over here we are initializing an empty vector right then we want to iterate here over a range and in each iteration we are just pushing n to the vector now in the end the length of this Vector should contain 100 elements right so what we can do over here is going from 0 to 100 meaning 100 is excluded going from 0 to 99 meaning the vector will then hold elements from 0 to 99. like that right 100 elements and this will compile next Method All iterators Implement a trait named iterator that is defined in the standard Library as you can see this is the iterator trade that is defined in the standard Library it has an Associated type item which basically refers to the type we are iterating over for example a vector of u8 then the item type will be u8 right and as you can see each type that implements the iterator trade will need to will implement the next method taking a mutable reference to the instance and returning an option containing the type of the associated type defined here and we can call the next method on iterators directly so over here we have a vector and we can then put that into an iterator right so this will then actually hold an iterator right and not a vector anymore so we put this collection into an iterator meaning we then can use the next method that is implemented for the iterator type and that is actually exactly what is happening when you are doing a for Loop for example in the back the rascompiler will just call next all the time until it reaches a point where there is a non-return because remember this next method will return an option type so when we put that into an iterator and call next we will get a sum and the first element right then sum and two and as you can see we don't have any more elements so it will return Norm right this is the option type and we have covered that and when we put a collection into an iterator it should be mutable right so the next method takes a mutable reference we have seen so we have to make it mutable all right into iter eater and iter mute in the previous section we have mentioned that 4 will apply the into ether to the collection and change it into a iterator however this is not the only way to convert collections into iterators into either eater and intermute all of them can convert a collection into an iterator but in different ways so into ether consumes The Collection once the collection has been consumed it is no longer available for reuse because its ownership has been moved within the loop ether dispers each element of the collection through each iteration thus leaving the collection untouched and available for reuse after the loop and it remute this mutably borrows each element of the collection allowing for the collection to be modified in place so as you can see we have here a vector then we Loop over this Vector here right and all we do here is actually printing out each of the elements now we have seen that implicitly the compiler will call here into iter meaning the iterator will become the owner of this data right so we can't reuse these are after we have put it into this iterator but there is not a solution we can just use either here because it's sufficient that we have a mutable because it's sufficient here that we have an immutable reference right we just want to print it out we don't need to mutate anything or anything else we only need read only data right so let's see and as you can see we then iterate over each element printing it this over here and then we can reuse the collection over here so ownership hasn't been transferred into this iterator five fill in the blank so over here we have a vector with string literals okay and then over here we are iterating over the elements in this vector now as you can see we are here matching the element and if it is immutable reference to the string literal fairies then we will actually modify the elements that we are iterating at this exact moment to this over here if it is anything else then it will be replaced by hello right so in the first iteration name will hold a string literal this over here then we match this and in this case this arm will match meaning we assign string literal hello to this element right so instead of Pop we would then have hello in the first index right so as you can see we are mutating meaning we can here use either mute right we want to take mutable references from this vector but as you can see over here we are again using these names Vector meaning we can't take ownership so let's see and this is compiling and as you can see the first two elements got replaced by hello and the last one ferries has been matched here right name holds a mutable reference to the string slice Ferries and that's why this will get returned meaning it will be assigned to this element let's see exercise 6 fill in the blank as you can see we have here a vector now over here we want to put this Vector into an iterator right now before we do that as you can see this is the vector in the beginning and this is how it should look like in the end so we want to mutate the first element so in this case we take it as mutable references calling The Ether mute method right so we take from here mutable references of the elements in this vector then over here we can call the next method which will then give us back an option type of the first element right so we pet our match here some with the inner value and then what we can do because we get here a new to the reference meaning we can dereference it and assign it a new value like that so we get no output let's actually see the elements as you can see we changed the first element we have mutated it using ether mute right getting bad getting back immutable reference to the elements creating our own iterator we can not only create iterators from collections type but also can create iterators by implementing the iterator trade on our own types so as you can see we have here a custom type counter which has one field and we Implement here the associated function new basically just creating an instance and then over here we implement the iterator trade for counter right so first of all we have to give the associated type a concrete type in this case u32 right basically the type this field is holding then we implement the next method as you can see next takes a mutable reference to the instance and it returns an option that holds a type of the associated item here right it would be u32 in this case so if self.count is less than 5 then we increment the count field here and we return the actual count otherwise we return none so if self to account is bigger than 5 then it returns none so as you can see we are then creating a new instance of this counter type using the new Associated function and then as you can see when we call the next method on it right because we can do that because we have implemented the iterator trade for our custom type so when we do that we just get back T value of count as you can see when calling next then over here it checks is the self to count field less than five in this case it is right we didn't increment it so far so it should hold zero and in that case it will increment it by one right the count field over here gets incremented by one and then it returns it and of course packing it into a sum because we return here an option type so that means we get one in the next iteration we get 2 3 4 5 and then as you can see the count field holds a value of 5 meaning this won't get meaning this would evaluate to false and this will get executed right so we will get back a non and over here we have Fibonacci so the Fibonacci sequence starts at zero then one then zero plus one is one then one plus one is two then one plus two is three two plus three is five five plus three is eight and so on right and as you can see we have here distract Fibonacci that has a current field and a next field so as you can see current for example would hold zero and next would hold one and as you can see we are implementing the iterator for Fibonacci so we Define here the return type to be u32 right or basically the type of the items we are iterating over so these both of these fields hold u32 and over here we have a function Fibonacci which just returns an instance of the Fibonacci struct with current holding 0 and next holding one so notice something here when we call next we actually want to return someone and not some zero so this is important but let's now implement the next method so we have seen that it will return option type holding self item write the type that is defined in the associated type item and then over here I will create a new variable forward and this would then be self dot current plus cells dot next right because so forward in this example would be zero plus one right this would equal one so we can then to go one step forward we want to go like this and then like this right we can assign to self current self next meaning in the next iteration the current field will hold one right and self.next will hold forward right self to current plus self dot next meaning in the next iteration next we'll hold one so we will be at this point right and then all we need to do is we wrap it in sum because again we return an option and we return the current a field over here let's see methods that consume the iterator the iterator trade has a number of methods with default implementations provided by the standard Library consuming adapters some of these methods called the method next we use up the iterator so they are called consuming adapters so over here we have a vector and we put that into an iter notice we take here the iter method meaning it will just take immutable references to the elements and then we call on this iterator this sum method the sum method will take the ownership of the iterator and iterates through the items by repeatedly calling next method right so all this sum method does it will just add together all the elements in the vector notice sum will take ownership of this iterator meaning we can't use V1 eater again right so in this case the total will be six now I can't edit here let's see it in the playground so we have here a vector and then we put it into an iterator and then we call the sum method on the iterator meaning this should then hold i32 and the value should be 6 right just adding all of these together now that means we can't use V1 either again here this is not possible because it is some method will take ownership of the V1 eater variable let's see as you can see this is compiling let's actually print out the result as you can see we would have six collect other than converting a collection into an iterator we can also collect the result values into a collection collect will consume the iterator so over here we have an array which holds two tuples right and each Tuple has as and each Tuple has a string literal and an i32 right then we take this names array here put it into an iterator notice here into either meaning we take ownership of these elements and then we collect meaning we collect it into another collection which has to be annotated here meaning we will then create a hash map with string literals as keys and i32 values right then we print it out here and over here we have a vector of i32 types and we call The Ether method over here meaning we just take references to this vector and then over here we call collect meaning it will get collected into an iterator right because we want here V2 meaning and then we annotate here that we want a vector of i32 because as you can see V2 should hold a vector with these elements let's see and actually over here we would have references of i32s right because again ether here takes references of these elements meaning they would not equate we want a vector of I 32 elements meaning we can call the input ether and that means ownership of these elements will be transferred to V2 and this would be the output of the print line over here we are printing here out the hash map we have converted iterator adapters methods allowing you to change one iterator into another iterator are known as iterator adapters you can chain multiple iterator adapters to perform complex actions in a readable way but because all iterators are lazy you have to call one of the consuming adapters to get results from calls to iterator adapters so over here fill in the blanks we have a vector and we iterate over the elements just taking immutable references alright then over here you can see we want to modify the elements inside this Vector to look something like that basically just incrementing each element now we can use here map to do that right Now map will create another iterator it will take ownership of this iterator here and it will then create another iterator right but with the modified elements and then we can call here collect to actually put it into a collection meaning this Vector over here let's see all right this is compiling I guess this would be i32 so we have taken it from this vector so this is compiling let's actually output as you can see we have modified the elements but of course V1 is still accessible and that is because we have called here the eater method meaning we just take immutable references of the elements in this vector meaning the ownership will remain at V1 so we have now reached the end of this course and if you have made it this far then your dedication to this language is really impressive and this is only the beginning of your journey I hope this course was helpful don't forget to split the word about free software and I'm out\n"