 So we've given examples of how to specify types, but to make this explicit, the rule is that you have your type, and if you prefix it with an asterisk, that's making it a pointer to that type. If you prefix it with square brackets that are empty, that's making it a slice of that type. And if you're prefixing it with square brackets with a number inside, a compile time expression that gives us back an integer, that is making it an array of that type. And we can use multiple prefixes in front of a type to get slices of slices, slices of arrays, pointers to arrays, etc. So here, for example, if we start out and we just have int, well, that of course is just the built-in int type, you put an asterisk in front and now it's a pointer to int. You put square brackets with four inside before the asterisk announced an array of four pointers to int. Put another pair of square brackets inside and now we have a slice of arrays of four pointers to int. And then put another pair of square brackets in front and now we have a slice of slices of arrays of four pointers to int. And then finally put an asterisk in front and now it's a pointer to a slice of slices of arrays of four pointers to int. Not that very complicated types like this are particularly common, but they are legal. So you should understand the syntax. You read it left to right. And one thing to avoid is you want to avoid confusing yourself by mixing what we can do in English. Instead of saying pointer to int, we could say int pointer. And in fact, that is probably more common to say. But you can easily confuse yourself like if instead of slice of arrays of four pointers to int, what if I said slice of array of four into pointers. So I mean, you'll hear people say things like that, but it's getting kind of confusing because we're no longer going strictly left to right. And I find that confusing. So if you want to get things straight, it's only best to be consistent and then go left to right, or you can go the other way, like say, for this last example, we would say it's an int pointer four sides, because a little awkward four sized array of slice, no, array slice, slice pointer is just a little awkward because we're using these stacking of a whole bunch of adjectives here. And honestly, I think if someone said this to me, I don't think I would follow what the hell they're talking about. Not that this form is particularly easier to follow. It just gets a little confusing when we say these things out loud verbally. So just be careful not to mix going left to right and right to left. That's a recipe for confusion. Anyway, so looking at pointers, this is nothing we didn't already do in GoPigeon. It's just the syntax is now different. So this main function here, we have a variable i, which is of type int and variable p, which is a pointer to int. And double p is a pointer to pointer to int, or sometimes as we say, a double pointer to int or in double pointer, you might say. So we assigned to i an int value of five, no surprise there. But what we assigned to p is we want to get a reference, we want to get a pointer value representing the address of some integer variable, some in storage location, and we can do so by using the ampersand, the reference operator on the variable i. Be clear, like conceptually, what's going on here is this operator, it's not really operating on the value stored in i, that's what most operators do, like you add the value of a variable to something else, right? You're taking the value stored in the variable, but here, the reference operator actually operates upon the variable itself upon the storage location itself, it gets its address. And so anyway, that's what's being stored in p. And then for double p, we are assigning it the address of p. And because p is a pointer to int, if we get the reference to p, what we get is a double pointer value, we get an int pointer pointer, or again, a pointer to pointer to int. It's unfortunate, that's so easy to confuse these terms. So anyway, double p stores the address of p, p stores the address of i, i stores the int value of five. And down here, we create a variable j of type int. And here, what we assign to j is the value dereferenced from p. We have this pointer p pointing to the location of i, and if we dereference it, then we're going to that location and reading the int value there, p is an int pointer. And so if you dereference it, what you get is the int value at the location it represents. So in this case, that would be the value five. And so j is being assigned the value five. And what we could also do is if we dereference double p, what we get back is the pointer value stored in p. So dereferencing double p gets us the value of p, which is a pointer referencing i. And so if we dereference that, then we get back five as well. So we don't actually need the prens here. We can just do it like this. And this dereference would be done first, and then the one on the left would be done second. So we don't really need the parentheses, but just to make it clear, we have the parentheses there. And then down here, we are assigning to the dereference of p. And what that means is we're not getting the value at stored at p. We're not really reading the value five from i, because that's what p is pointing to. So we're not we're not really doing that. We're just, what this really means is that we're storing the value four at the location represented by p. That's what the syntax means. And so effectively, because p is pointing to the location of i, we're storing the value four in i. And then on this next line, same deal, except we're assigning to the double dereference of double p. And because double p is the address of p, and that pointer value is referencing i, then this is the same as assigning to i. So this is effectively storing three in i. And again, I don't really need the parentheses, I was just doing so for clarity. But what's happening here is you conceptually can think of the, the dereference operator closer to the thing being dereferenced is performed first. And so, so, so you can think of this as first dereferencing or double pointer to get back our single pointer or in pointer. And then this operator is dereferencing that. But again, there's not really a dereference is actually storing a value at the location of the pointer. Anyway, this last line is a compile error, because we're using the reference operator on J. And that's valid, this part's valid. But then we're trying to use the reference operator on the pointer, which this returns. And you can't do that. The reference operator can only be used on things which are storage locations we can get pointers to which are variables. And then also, as we'll see on the next slide, elements of arrays and slices or fields of struct variables. And just quickly to demonstrate getting pointers to elements of arrays. Here we have an array R, which is a array of three ints. And we have a variable P, which is a pointer to int. And if I use the reference operator on this index into an array, then what I get back is a pointer to, in this case, the second int of the array. I don't actually need the friends here because the index operator has a higher precedence than the ampersand, but I just have it there for clarity. So this is the same thing. And then be clear, if I want to get a pointer to the array itself, that's not really the same thing. If I forgot to point it to index zero, that would be the starting point of the array, yes, but it would be considered to be an end pointer, not an a pointer to the array. Here, when I have a variable AP, which is a pointer to an array of three ints. And then I use ampersand and R. What I'm getting back is a pointer to this array. And that pointers type is different than an end pointer. This is going to be a pointer to an array of three ints. If I dereference AP, what I'm getting back is an array of three ints. Whereas if I dereference P, what I'm getting back is an individual int. I actually shouldn't have said that you can only use the reference operator on variables because as a special allowance, you can use it on a struct literal, like here on this cat we're creating, or you can use it on array literals, like here, or you can use it on slice literals. But we can't use it in another kind of literals. You can't use it on number literals or string literals or other kinds of values. Only these kinds of things here. So this is creating a cat stored somewhere in memory, and we're getting a pointer to that location stored in A, which is a cat pointer. This is creating an array of two ints stored somewhere in memory. We're getting a pointer to that location stored in this variable B, which is a pointer to an array of two ints. And then this is creating a slice of ints somewhere in memory. We're getting a pointer to that, which is stored in C, which is a pointer to a slice of ints. I don't really know why these are allowed, but these aren't, seems a little inconsistent to me. I do know that these cases are convenient, so I understand why they allow it. And these cases don't really come up very often. They're not something you would really want to do very often. But I'm not sure why you can't do it.