 In static pigeon our functions can return multiple values, so here for example we have this function zelda that's returning not just one thing but two things. It returns an integer and a string in that order, and we can have a function that returns three things or four things or five things. That's not terribly common, but it is legal. Anyway, having declared that zelda returns an integer and a string, that means that all the return statements in zelda have to have first a number value, an integer value, and then a string value in that order. Otherwise it would be an invalid return statement. The compiler would give you an error. Now when we call zelda, if we don't want to do anything with what zelda returns, that's okay. We can just call the function and then just discard what it returns. But if we are going to use what zelda returns, the only way to do it is to call it in a so-called multi-value context, and what that means is you have an assignment where there are multiple targets. We haven't seen this before. This is an assignment where we're signing to both x and y in that order, and then the value is what zelda returns, and zelda's returning an integer and a string. So this is okay. What we can't do, what you might assume we should be able to do, is something like this where zelda's just returning an integer and then we ignore the string. This is not legal. Nor can you do this where en as a function expecting an integer and a string, and hey, that's what zelda returns. But no, you can't do this. You can't directly take what zelda returns and pass it on to another function. The only way to have what zelda returns passed on to en is to assign the values to some variables and then pass those variables to en. A structure for structure is a data type that we defined in our code. We give it a name, in this case Ronald, and then we specify what elements, what fields, as we say, make up this data type. Here Ronald has one field which has the name foo and the type string, and another field which has the name bar and the type float. So every Ronald value is made up of these two things. Every Ronald value has its own string foo and its own float bar. There isn't really a sense of order amongst the fields, so the fact that we put this field first and this one second doesn't really have any consequence for what Ronald represents. There's no sense of order amongst your fields, except when we create values of this type, we do so by using the type name as an operator and then we list values for the fields and we do so in the order that we wrote them here, top to bottom. So we put the string first before the float because we put the string field before this float field. Otherwise, the order doesn't really matter. That's just a syntax thing, so it knows which value plugs into what field. Because of course it is actually possible you can have multiple fields of the same type. We can have an act field which is also a string. There's no limit on that. As long as they have different names. The names have to be unique. That's all that matters. Anyway, so in main here, we're creating a variable R of type Ronald and to access the fields of a struct value, we use the get operator and you specify the struct value, which you want to get a field from and then the field name and be clear. This here is an expression. This in this case is a variable R. It could be some function called it returns a struct, a Ronald struct. It could be anything that gets us a Ronald value. But then when you specify the field name, that's not an expression. That's really just the name plugged directly into the operation. And because the compiler knows, looking at this, it knows that this expression returns a Ronald value. It knows what the acceptable names are. In this case, Ronald has two fields foo or bar. So if you wrote some other name here, you'd get an error at compile time. Anyway, so if we get the foo field from this Ronald value, then we get back an empty string. If we get the bar field of R, then we get back 0.0 because we haven't assigned anything to R and the default value of a struct value has default values for all the fields. So the default value of a string is empty string and the default value for float is 0.0. So that's why we get back these values. But then here, this operation is creating a Ronald value, which we're assigning to R and that effectively copies all the respective fields from this value to R. So this is just a handy way of initializing all the fields of R in one go. So now when we get foo of R, we get back the string high. And to modify the values of a field, we use set. So we say set. This is an expression, which is some struct value, in this case, the variable R. We specify the name of the field we want to modify and then the value for that field. And again, this is also an expression. It's just anything that in this case gets us a float. The compiler will complain if we put the wrong kind of value here because it knows what type of thing bar is supposed to be. It knows that R is a Ronald and Ronald has this field bar, which is a float. So if we made this a string or something else, anything not a float, we'd get a compilation error. Now here's a slightly more realistic example of what a struct might look like. Here's a struct representing a cat. And we'll say that our cat is made up of three things. It has an age, which is an integer, a weight, which is a float, and a name, which is a string. And then we can define this function eat, where one of the parameters, one of the inputs, is a cat. And what we're doing with a cat is we're feeding it food. It's eating food. And the food is just some float number representing an amount of food in terms of its weight, pounds or kilograms, doesn't matter. Anyway, so when a cat eats, its weight increases by the amount of food it consumes. So what we're doing is we're getting the weight of our cat, adding that to the weight of food and then setting that to be the new weight of the cat. And understand that the set operator returns the value being set. So it's modifying the weight field of C, but then it's also returning effectively this value. And so that's what this function eat returns when we call it. So down in main, if we create a cat variable C and then initialize it, so it has these initial values, 10 for the age, 8.9 for the weight, and the string mittens for the name. If we then call eat on C, passing in 0.7, what we get back is 9.6, because in the function it's taking the weight of the cat, which is 8.9, adding 0.7 to that, we get 9.6, and that's what's being returned. But then after eating, we look at our cat's weight, and it hasn't changed, it's still 8.9. So what's going on here? Well, you need to understand that when you pass this cat C in main to the parameter C of eat, first off be clear, these are separate variables, right? So there's separate cat values. What's actually happening in the call, and this is basically true for all arguments to functions, is that the argument is copied to the parameter variable. So this C is copied to this C such that if we modify C and eat, the parameter C and eat, that has no effect on the C out in main. They're really just separate things. Now there are scenarios where you'll want to pass something into a function and have that function modify the thing as seen by the caller. The caller wants something to be modified by a function. And a function can do that when it receives something passed by reference, which is what happened with lists. List variables as we discussed really are just storing references to the actual list so you can have multiple variables all pointing to the same list. So if we want to pass in a struct or other kind of thing by reference, we're going to need to be able to create references to structs and other kinds of things. And as you'll see later, we can do that with what are called pointers. First though, I want to talk about methods. Methods are a minor variation of a function where the first parameter as a rule has to be a struct type. It can't be any other type in static pigeon. And we say that a method belongs to the struct type of this first parameter. So here this method eat, it belongs to cat, and this method eat belongs to dog. And these two methods can coexist just fine because they are methods of different types. Even though they have the same name, normally that would be a problem, right? You can't have two functions called eat. But this is okay because they're methods of different types. The cat type can only have one method eat. The dog type can only have one method eat. Any number of types can all have a method called eat. And in this case, the eat methods look similar. They have the same argument and return type and basically the same code. But that's just coincidence. You can have methods of the same name for different types that do totally different things. That's entirely up to you. But anyway, in Maine, here we have a local C of type cat and a local D of type dog. And we can call our eat function like normal. That's this thing up here. It's expecting a cat and a food value and a float value. But to call the methods, we can't just call them like normal. We need the MC operator, the method call operator. And first you specify the name of the method and be clear. The name you specify is not an expression. It's just the actual name, just like with get and struct fields. And then you have all the arguments that you want to pass to that method. And the important thing here is that the compiler knows which method we're talking about. We have two eat methods here, but it knows for each call which one to invoke based on the type of the first argument. Because here we're calling eat where the first argument is a cat. It knows that we want to call this method. And in this line on the second time, the first argument has the type dog. And so it knows that we're calling this method. Now you're probably wondering, what is the point? Because as described so far, methods aren't really doing anything useful for us. We could just create normal functions and give them different names. It's just a very small, stylistic allowance so we can have these methods of the same name. Well, what makes methods actually useful is their connection to what are called interfaces. And interface is another kind of data type, which we define at the top level of our code. And because it's a data type, the names we give them are uppercase letters. So this here is to find in an interface called Jack. But what comprises an interface is not elements of data, but rather a list of what are called method signatures. A function signature is everything about a function which the caller needs to know about the function. So it's the name of the function so that we can call it by name. The parameter type so we know what's passed to it. And then the return type so that we know what we're getting back. But everything else about the function, the names of the parameters rather than their types, we don't care about that. And also the actual body of code, that's not part of the signature. That's the business of the function itself. The signature is just what we need to know as the caller. So for a method, a method signature is the same deal. It's everything we need to know about a method to call it minus the names of the parameters and the actual body of code. So here in interface Jack, we have in no particular order, we have a list of method signatures. The order doesn't matter. We can swap these around. But here first, it's a method signature where the method is named foo. And then the input types are integer, then string, and we return string. And a method bar that takes nothing and returns nothing. So that's the two methods here in this Jack interface. And then if we have some struct type, which has methods matching all of these signatures, then that struct type is said to implement the interface. So here, for example, we have cats. So here, for example, we have the cat type and the dog type with both of those methods of the interface Jack. So both cat and dog are implementing Jack. They could have other methods. It doesn't matter though, for the sake of implementing the interface, as long as they have these, then they implement the interface. And notice that the actual struct type, of course, is present in the methods. And we specify the method signatures up here. You don't specify the struct type because that differs for the different implementing types. Dog gets plugged in here as the first argument and cat gets plugged in here. Now the significance of a struct implementing an interface is that, down here, we can create variables of an interface type. And then having done so, what that variable can store is a reference to a value of an implementing type. So because cat implements Jack, we can assign cat values to a Jack variable. So we can assign C to J here. And what's happening is, well, implicitly, it's the same as this. You can take a cat value and from it create a Jack value. That's what this is saying. You don't ever really have to write this though, because as far as the compiler is concerned, implementing types, implementing structs are considered valid instances of the interface type. A cat is a Jack value. So you don't have to write this. But you could. And what's going on here is that J is storing a reference to the cat value. But then it's also storing a reference to the cat type. And the way this is represented in memory is that because of how method calls are implemented, there are cases where at runtime it needs to look up where the actual method is in memory. So for every struct type, there's a so-called method table, which is a list of all the methods of that struct type and where they're found in memory. Anyway, we can use a reference to a struct's method table as in a sense a representation of the type itself. And so that's what these interface values do. An interface value is a reference to an actual instance of the struct, an actual value of the struct, but then also to its method table. And that's important because when we assign this cat struct J later on, in some cases, we want to know what actual type is stored in J. If we just had a reference to the struct value itself, then, well, because it might be a cat, it might be a dog, the reference itself wouldn't tell you what kind of thing it is. And so we need to also, in a sense, include a record of what the actual type of the thing is. So that's why we also have a reference to the struct's method table. Anyway, also understand that initially, because a interface variable is, in a sense, a reference and its initial value is nil, it points to nothing. So right here, before assigning anything to J, initially, it'll just have the value nil. Now, what can we do with these interface values? Well, as far as the compiler is concerned, it doesn't presume to know what J is other than it's a Jack. It doesn't know concretely whether it's a cat or dog or some other type that might implement the interface. As far as it knows, it's just a Jack value. And so all the compiler will let you do with a value of type Jack is what it knows is valid for type Jack, and that is calling methods of this interface because it's guaranteed that implementers of an interface will have those methods. And it's known for sure that whatever J is here, whether it's a cat or a dog, the compiler can know for sure that, well, it's going to have a foo method and it's going to have a bar method matching these signatures. So that is the significance of interfaces. Interfaces, in a sense, are representing the set of methods which other types will have in common. You have some set of structs, and they have some set of method signatures in common. An interface lets you express that commonality and tell the compiler, hey, all those different kinds of things, they're different types, but they all have this set of methods in common. And so if we call the method bar on J, what this does is that compile time, again, the compiler does not know what J is, it just knows it's a Jack. But at runtime, the language will look at J and see that, oh, it's pointing to the cat method table. So what it references must be a cat. And so this is calling the method bar of cat. So in this case, actually, it would be equivalent to if we wrote MC bar C. That would be exactly the same result here. Actually, there is a subtle distinction here. When we assign a struct value to an interface variable, it doesn't create a reference to that actual variable, it creates a copy of the value. So having to assign C to J here, you would think, well, if J is referencing C, then if I modify C somehow, if I don't know, set its name to something else, set its name to Oscar, that's the name of my cat. You would think that this would modify what J is referencing, but no, it's just modifying C, because J is actually getting a reference to a copy of C, so it's a subtle distinction there. Anyway, what the compiler won't let us do with J is call any other method. Even though we can look at this code and trivially say that, well, of course, it's holding a cat, and so it should be okay to call the meow method. Assuming cat has a meow method on J, that should be fine, right? But no, the compiler doesn't let us do it because it can't, for certain, say that J is going to be a cat. We can look at this code and know that J is going to be a cat when this is reached, but there are many cases where imagine like this in between here, there's a bunch of branching logic where depending upon what happens, J gets assigned a cat, but then another branch gets assigned a dog. So these cases where, in most cases, actually where the compiler can't know what an interface value is actually referencing, what concrete type it has, that compiler can't know. And so it doesn't ever presume to know, even though in a case like this, trivially, it could see, yeah, it's just holding a cat. But because in many cases, it can't know, it just doesn't presume to ever know. Anyway, so the compiler will reject this, even if cat has a meow method. This is not valid because the jack type doesn't have any such method. And similarly, we can't access the fields of our cat when it's stored in a J because, again, as far as the compiler is concerned, the only thing it knows about jack is that it has those two methods. It doesn't know what fields it has. It could be, you know, because concretely, if it's a cat or a dog, they can have different types of things, different fields. So it wouldn't know for sure that whatever jack is that it actually has a name field. So this is not allowed either. And then here we're assigning to J the value of D, this dog. And again, actually strictly speaking, it's creating a copy of that and storing a reference to that copy of the dog struct value. And now, because J is referencing a dog, because it's referencing a dog struct and then also referencing the dog method table, when we call MC bar J here, it knows, looking at J, that it's a dog. And so it calls the method bar of dog, not cat. Because it knows what concrete type at runtime. It knows what the type of J is, what it references. So that's how interfaces work. Now, why are they useful? Well, one simple example is that, say, we want to create this function feed, where it takes this input, this interface type we've defined, which is called an eater. And an eater is defined to be anything which has an eat method that takes a float and returns a float. And so our function feed can take as input any type of thing, any kind of struct that implements this interface. And we don't have to care the differences between the different actual structs. We just care that it has this method eat so that for our parameter E, we can call this method passing in a float value. That's all we care about. So this gives us some flexibility because otherwise in a statically type language, when we specify what the type of something is, it has to be one type. Well, these interface types are effectively types that encompass potentially multiple other types. You can have a single interface that has many struct implementers. And so the actual concrete type at runtime of what this eater is, can vary. Admittedly, this is really a total non-issue in a dynamic language. While you would have in a dynamic language, you'd have no interfaces. You just have your feed function and some parameter E. And you would expect that whatever E gets passed in has to be something valid to pass to some function called eat. That's all that would be required there. And that would be a responsibility of the eat function to make sure that it knows what to do with whatever you pass into it. But there's no issue with the compiler fighting with us and not wanting us to pass in whatever we want to a function. So interfaces are sort of a way around the strictures of static typing. They account for a very common case where you want to have this one type, which is not actually just one type. Anyway, there are also cases where you have a value of an interface type. And aside from just calling the methods of that interface, you want to get at concretely the actual value referenced by that interface value. And we can do so with what's called a type switch statement. So here, for example, we have this function Alice, where we have this parameter J of type Jack. Assume again, Jack is an interface and it has implemented, let's say cat dog bird and potentially some other types as well. We don't care about what they are, but it has some other implementing structs. And so if we want to get whatever J is as a cat and use it as a cat, well, the compiler doesn't let us do that unless we use a type switch. And the type switch looks like this. You specify this expression here is just the interface that we want to get the value out of the thing it references. And then it's going to store that as V. And then we have different cases. For the case where J is a cat, then we do this business. And inside here, the variable V is going to be holding that cat value. But then we also have the case for dog where J was storing a dog. And so we can do this action. And in here V will be a dog value and likewise same for bird. And then in the event that it's not any of these three, maybe there's something we want to do in the event it's none of them. So we also, you can have a default clause at the end. This is optional, but you can have a default clause. And this gets executed if the type of J is neither cat, dog, or bird. And in here, the value of V is going to be still a jack value. But sometimes it's useful to just make a branching decision. You want to test, well, what type of thing actually is this? And in the event it's not one of these other things, well, you want to do something to handle those other cases. So that's a type switch. It's a kind of statement. It's just like say an if or while is a kind of statement. And effectively these are mutually exclusive cases like in an if, else, ladder. You have a bunch of cases where it might be cat or it might be a dog or it might be a bird. It can't be any of those things at the same time or it's something else. So these are all mutually exclusive. Now there's a special interface which is built into the language called any. And what's special about the any interface is that it actually has no methods whatsoever. There are no method signatures. And so every type, all of your structs are considered to implement the any interface. And in fact, the other special thing about it is that even non-struct types, the built-in type, string, integer, lists, arrays, whatever, everything is considered to implement the any interface. And so given a variable of type any, we can assign to it anything, any kind of value. And it's going to store a reference to that value and then also store another reference which effectively signifies, keeps track of what kind of thing it is. So that later on, if we want to do a type switch, we can concretely get what the thing is. So here, for example, we're creating a variable a of type any and we can assign this number 35. We can explicitly cast, we can create an any value out of 35 using any name as an operator. You can do that, but it's not really ever necessary because everything just automatically is understood by the compiler to be a valid instance of the any type. So we can just directly here, like assign the string yo to a or the boolean value true to a or some cat, we can create a cat and assign it to a. So these are all valid. Just keep in mind that with this any value a, what can you do with it? Well, you really can't do much of anything because it's not known to be a string or boolean. As far as the compiler is concerned, a here is an any type. And what can you do with any? Well, it doesn't have any methods. You can't call any methods on it. All you can do is treat it as an any. So you would need like say a function that's expecting an any as input and then you could pass this any value as argument to that parameter. But otherwise you really can't do anything with any. There aren't any operations that work on any values. Also understand that any is a type like any other. So you can plug it in wherever a type is expected. And so like here, for example, we're creating an array of five any's. And so when we set values into this array a, well, that can be of any type here. It's a string, here it's a float, here it's a boolean. This is all valid because the compiler says it can be anything. Also understand when you create structs, the fields can be interface types. So like assuming again, we have our interface type jack, you can have a field of type jack. You can create a field of type any. Interfaces, including any are types like any other. And so you can specify them by name where a type is expected. And this demonstrates another case where interfaces can be useful because sometimes when you're creating your structs like you're creating a struct representing, I don't know, a person. And let's say every person has a pet. Well, there are different kinds of pets. And maybe if you had a struct representing a cat and a dog and a bird or a snake or whatever. Well, the problem is those are different types. So what kind of type should the pet field be? Well, one solution to that problem is that you can create an interface, say animal, which encompasses all those other types. They can all implement the animal interface. And thereby you kind of a field of your struct for your person where the pet can be an animal. And then what that animal is can vary. It can be a bird or a dog or a cat. There are cases where we need a piece of data to sometimes be this one type but then maybe this other type or this other type. And that's what interfaces provide us. It's a data type which concretely can be one thing or another thing or another thing as long as all those things implement the interface. Lastly, understand that we can use type switches for the any type. And when we do so, these cases for other interface types the types you specify in each case have to be struct types. But here we have cases where it's built in types like integer or string or could be like array of string or just any type that's possible with any because the any interface can encompass things that aren't structs. I mentioned in passing earlier that we have what are called pointers and pointers are basically just references. They are memory addresses pointing to values elsewhere in memory. But the very important thing about pointers is that they are typed. Meaning you don't just have pointers pointing to anything, you have pointers to strings, pointers to integers, pointers to floats, pointers to whatever, and those are all considered to be different types of pointers. Just like when we create slices, lists and arrays, they are arrays and slices and lists of particular type. They're not just considered the same kind of thing. So here, for example, in main we're creating a variable I of type integer and then also variable P which is a pointer to integer. That's what this denotes. And what this means is that I can store in P I can assign to P values which are addresses of integers, not other kinds of things. And so the way we get a pointer value is by using the rough operator and the argument to rough, the operand of rough is not an expression that has to be the name of a variable. So here it's the variable I. It could be like some function returning an integer. If we had some function foo that returned an integer this wouldn't be valid because you can't get the address of values, you can get the address of variables because variables are storage locations. So this is valid. And so now P is going to be storing the address of the variable I. We then assigned to I the value four and here if we attempt to add the value of P to seven this is not valid because P itself is not directly storing an integer value it's storing the address of some integer elsewhere in memory. And so this is not considered valid. You can't directly just use the value pointed to by using the pointer itself. That's not considered valid. If you want to get the value pointed to by a pointer you have to dereference it and for that we have the dereference operator and so the operand for this is a pointer value. So in this case of the variable P. So this is getting the value at the location referenced by pointer P and then adding seven to it and returning it in this case we're gonna get the value 11. We can use the set operator to modify the value at the location represented by a pointer. So here P is pointing to I and if we set two to P that effectively is the same as assigning two to I. And so now if we print the value of I after setting P to two it's gonna be two. So through pointers you can modify the locations they point to. You can change the value stored there. The default value for a pointer is nil. So here we're creating this variable P which is a pointer to float and it's gonna start out with the value nil before we assign it anything and keep in mind that you don't wanna try and dereference a nil pointer. That doesn't make any sense because it points to nothing. So you can't get the value pointed to by nothing. So if you try and dereference nil pointer you're gonna get a runtime error. The compiler can't know for sure what the value of any pointer variable is. So it can't know when things are nil. It doesn't ever presume to know even in cases where it could check. But the language does check at runtime. Every time you dereference a pointer it checks to make sure it's not nil. And if it is nil you get a runtime error at the program of boards. Okay, so now why would you want to use pointers? Well, there are a few reasons. One is that by using pointers there are references and if you have multiple references pointed to the same value then a change to the value is seen by all of those references. And sometimes that's useful. This is a very artificial example but it does sort of illustrate the point that if we have here pointer one, pointer two, pointer three and they're all referencing in the same local variable i. They all have the address of i. Well, if we modify i that change is seen through all of these pointers. If we dereference p1, p2 and p3 each time we're getting back nine because they're all pointing to the same location and memory. There are scenarios where this is very useful because you have this one thing in code that from other parts of code you want to be able to access that value and modify that value and you want the different parts of code to all see the same change. If any part of code changes that you want the other parts of code to also see that change. So that can be useful especially across different functions. So be clear like you can pass this argument a pointer value to another function and you can return pointers from functions. And so you can share references to pieces of data to locations and memory across different parts of your code and sometimes again that can be very useful. Another use of pointers and arguably this is actually just a variation of what I just described. But a very common scenario is you want to pass something into a function and have the function modify the thing such that the caller sees the change. Well, if you just pass an ordinary value like an integer into a function what the function is actually getting is a copy of that value. If it changes it in the function the caller doesn't see that. But if we pass a pointer into a function in the function by using set or a dref then that function by using set can modify what's stored at the location which the caller sees. So here for example we have this function foo which takes in a pointer to an integer and then it sets the value at that location of the pointer to be 11. Well then in main if we have this variable z which is an integer initialize it to five and then call foo passing in a pointer to z. Well then after the call to foo the variable z now has a new value it has the value 11 because this function foo actually modified the variable in the calling function main. So that's a very common scenario of how pointers can be used. The last big reason that we use pointers is that memory addresses are always the same size. Within a single system at least if it's a 32-bit system all your addresses are gonna be 32 bits in size they're gonna be four bytes on a 64-bit system all your addresses are gonna be 64 bits they're gonna be eight bytes in size. And so if I have an address and it's a address of a very large struct or a small struct or a very large array or a small array that address is always gonna be the same size it's just four or eight bytes. It's relatively small compared to much larger things like big arrays. And so one reason to use pointers is that we don't wanna have to copy very large things we wanna avoid that overhead because it can get very expensive and bog our program down. So instead we use pointers and so here for example imagine cat is some very large struct type rather than having our function take a cat itself it just receives a pointer to a cat and we have to be careful then and make sure that and understand that if we modify the cat then the caller will see those changes we have to make sure that's what we want but it is more efficient to pass in just a pointer to a cat rather than the cat itself because otherwise if the C parameter here was just a cat then the cat value would have to be copied in full every time we call this function foo. And again if the cat is a very large type that could be expensive. Likewise here if this function bar if it's nums parameter we're just directly an array of 50 integers well array of 50 integers that's quite large every integer it's either four or eight bytes and multiply that by 50 so it's quite large at least compared to a pointer it'd be cheaper if our function bar took as input a pointer to an array of 50 integers and that's what this is and so again that could be more efficient it avoids that big copy though I should be clear when dealing with arrays in static pigeon because we have slices and slices effectively represent references to arrays we use slices much more commonly than we use pointers to arrays you can have pointers to arrays but much more commonly we'll just use a slice instead so this is more realistic in that particular case but for structs we don't have any analog of slices so we would use pointers last thing about pointers is that we can use rough to get pointers to fields of struct variables and indexes of array variables and so here for example we have this cat variable c and we're using rough to get a pointer to the name field of the cat which is a string so p here is a point of the string so this returns a point of the string value referencing name within the cat variable c and that's what stored in p is that location and then down here we have an array of 10 integers and a pointer to integer and so because the array is composed of integers if we get a reference to index two of the array we're getting a pointer value pointing to that location within the array and it's a pointer to an integer be clear though this is very important and it's a subtle thing the first operand to ref and in all cases across the board has to be a variable it can't be just a value it has to be a variable that means that you can't have something like this where say this function foo returns a cat and we call foo and we want to get a pointer to the name field of that returned cat value the reason language just allows us the reason it only lets us use rough to get locations of variables and also fields and indexes within variables it's really just implementation details the way the compiler converts our code into machine instructions the way it implements function calls if it allowed for something like this it would be very inefficient it would slow the whole code down so just to avoid those inefficiencies or something that you would really not really want to do very often anyway it just disallows it it's not common you would even want to do this so just understand that you can't we can only use rough on variables we saw in dynamic pigeon that we can treat functions like values meaning we can store them in variables we can store them in lists and maps we can pass them into functions we can return them out of functions, et cetera we can do the same in static pigeon but to satisfy the compiler we have to distinguish between different kinds of functions what kinds of inputs a function takes and what it returns that's part of the type of the function and so here if I create a variable f to store a function I'd say it's a function variable but then I've specified what kind of function and in this case it's a function that takes an integer and a float and returns a string and therefore what I can assign to f here is any function with this set of inputs and outputs like bar here for example bar matches so this is an okay assignment we can assign bar to f and if I call f with arguments eight and 2.0 then it's calling this function bar but what I can't do is assign some other kind of function to f foo here for example is not quite right it's missing the return type of string it returns nothing and so it would be invalid to assign foo to f so the compiler will not allow this assignment and do understand that these function variables a function as a value is actually a kind of reference the function exists somewhere in memory and what the variable storing is really just an address of where that function is located and so if you create a function variable its initial value its default value is nil and just like with pointers it's invalid to try and use a pointer that is nil well same if we try and call a function that's nil so f here while it's nil if we try and make this call we get a runtime error and again like usual the compiler never presumes to know what the actual value of any variable is it knows the type but it doesn't know the value so even though trivially we can look at this code and say well of course f is gonna be nil the compiler doesn't check for these cases and so it doesn't stop us from compiling and running this code but then at runtime f will be nil in this case and so this call will be invalid and oops I have a wrong type there there now it's a float and now it's good but this will be a runtime error here's an example of a function which takes its input a function for its first argument so the parameter a here its type is a function taking an integer as input and returning a string as output the second parameter is just a normal integer parameter and then the return type of Jared itself is a function which takes nothing and returns a float so it's a little confusing to look at this I know because of all the colons and angle brackets all over the place but that's what's going on Jared has one input, a second input and then the single return type so when we call Jared the first argument has to be something which is a function taking an integer as its only parameter and returning a string as its only return value and what Jared is going to return must be a function that takes no inputs and returns a float and the compiler will enforce that