Welcome back. It's another crust of rust mini session. I really like that name. It's a good name. So we did the previous one on lifetimes and in particular sort of lifetime annotations and when you need lifetime annotations and when you don't. And to sort of continue this trend of focusing on a particular use case and trying to write like real code that needs this particular pattern, I wanted to take a look at declarative macros. So nothing, no like proc macros or anything like that. I have a video on that, but that is sort of a more advanced concept. Whereas here, because crust of rust is sort of intended to be like, let's deal with like one piece and just like do something with that piece and hopefully something that's understandable to a broader range of rust developers, even if you are a little bit newer to the language. Like this is something that is really useful to know about. And so we're going to be talking about macro rules. And just before I jump in, I want to sort of point out you can, if you like these videos, you can follow me on Twitter or YouTube or Twitch or wherever. And I'll put in particular on Twitter post whenever I'm about to make new videos, especially for crust of rust. I'll make sure to like announce them well in advance so that if you want to watch it, then you can just jump in. I will also upload all of these to YouTube afterwards. So I have a YouTube channel where I upload basically all the videos I do, both the longer sort of rust live coding streams that are a little bit more advanced, but also these shorter crust of rust sort of isolated episodes. And the macro we're going to implement today is perhaps unsurprisingly also from the standard library. You'll remember last time we did this sort of stir split thing, which was turned out to be something that was already in the standard library. And this was sort of by design because the standard library exemplifies a lot of real use cases. And so replicating things that are there is almost inherently going to be something that real people need. And I want to look at the VEC macro. So you may have seen this either in the form of like declaring a macro, declaring a vector using the sort of vector literal syntax, but you might also have seen this version where you give some element that is clone, and then you say how many of those you want. And what this resolves to is a vector that has all those elements. Great. So what we're going to do is basically write this macro. So you'll see that this is sort of the general syntax of the macro rules, which is you say macro rules, and this is how you define a new macro. And the next thing is going to be its name. So this is how it's invoked, followed by exclamation point.  So the patterns for macros is they're basically, they're arguments to the macro, but the arguments to the macro are much looser than arguments to a function, for example, here, instead of having like a variable followed by a type, what you have is sort of a syntax pattern, where you anything that's variable, anything that is sort of provided by the user is going to be one of these dollar variables that has a type. And the type here is not a rust type, it is a sort of syntax type. So this can be something like it can be an identifier, or it can be an expression, it can be an item, it can be a block. It can also be something like the name of a type, or the path to a type, or the path to a module. We won't dive too much into all of the different types here. What I want to teach you more about is like, how do you think about macros? What do they expand to? And sort of internally, how do they work? What do they do? And how can I write useful macros if there's something that I want to express? We'll get to a bunch of this in the process of implementing vector. I highly recommend that if you're going to be writing vectors, take a look at the little book of rust macros. This is just, it's a fantastic little gem of all of the weird little like oddities of macros. It explains a lot of the syntax, it explains many of the sort of quirks of it, but it also goes into a lot of really handy patterns. If you see section four, section four basically goes through, if you want to do something that does something like this, here's a pattern you can use. And we'll actually see some of that in our implementation of VEC. Before we dive in, are there any questions about like what we're going to cover or the vector macro in particular? Let's do that first. Dark theme for documentation, yes, I can. Nice. If you just search for the little book of rust macros, it should be the first result and keep that open over here. All right. So we're going to do the same thing we did last time, which is going to do cargo new lib. And we're going to call it VEC Mac because why not? And we want just a dedicated library for this in part because it's going to be easier to develop in isolation. You'll see this a little bit later. But also just because it's good to get into the habit of making libraries. So let me just construct a couple of terminals here for that and dive into source lib. I'm going to get rid of these. And so we want macro rules, we want a VEC. And notice here that I'm giving curly brackets for this. Macros are a little weird in rust in that they have optional delimiters, not in the sense that you can omit them, but in that the caller can use any of multiple types of delimiters. So if we write VEC like this, and let's say we just have an empty pattern, this is sort of the empty macro, then someone can choose to invoke it either as VEC like this or VEC like this or VEC like this. All of these are valid invocations. You'll notice that like this on its own does not work. And to see why, let's try to run cargo check on this. All right, fine. Yeah, so this is fine. It expands to nothing. And nothing is allowed to be in these locations. And usually the Rust format actually changed these delimiters because it knows about the name VEC. It knows that then if you have a like it has basically hard coded the name of the VEC macro to say always use square brackets for this, you'll see this if we define something else, like if we give it a different name, which we might want to so we don't conflict with the standard library version. I wonder why the curly brackets do not need this. Then you see now it did not do that replacement because it doesn't know that this this A VEC macro we've declared should probably also be formatted with square brackets. These are just optional and you in the current macro rule syntax, you don't have a way to say you must use a particular delimiter. Yeah, so we got a question about like sets and maps and stuff and we'll get to that a little bit later in the stream.  And one thing you need to know here is that the patterns are patterns over Rust syntax trees. They're not argument lists. So while you can do like arg one is a type, arg two is an expression, and arg three is a path, right? You can do this. And notice now it says like we're missing tokens in macro arguments, you can totally express your arguments this way, but you don't have to. So in macros, you're allowed to introduce whatever you want to syntax. This might not be. Yeah, let's do this. I'm going to get rid of these because they're no longer that useful. So with this macro, I can give whatever syntax pattern I want here as long as that pattern is valid Rust, it basically means that you need to write a pattern where the input is like a syntactically valid Rust program. But that is the only restriction. Apart from that, the input syntax can be whatever you want, right? Like if I were to try to write something that matches what I wrote above, one example of that would be maybe U32 to x dot foo semicolon standard path. Right. So you'll notice this compiles just fine. And this is not valid Rust syntax. If you if you try to compile this program, it the compiler would go no, but it is syntactically valid. It can be parsed. And so all that matters for the input to a macro is that it can be parsed. And then what matters for compilation is whether the output is valid Rust. So the input has to be syntactically valid. The output has to be valid Rust because the output is what actually gets compiled. Basically, you can think of every macro invocation as being replaced by whatever this input was. When you're working with macros, there's a really handy crate. It's basically a cargo command called cargo expand. So you can install this with cargo install cargo expand, and then you can invoke it as cargo expand. And what it does is it takes as input the source for the crate you're currently in, and then it expands all the macros with their definitions. In this case, you'll notice if I run cargo expand, the output here is like it has the prelude. This is what Rust injects into every Rust program. But it doesn't have anything about our macro. And you might think this is weird, but because we have this here, but it's because the replacement is empty. We could try to make it not empty, right? So we could, for example, here, say, let's get rid of this pattern and say that it's going to be ident. So ident is an identifier. And now we could say type arg2 is equal to arg1. Now this no longer needs a semicolon, and this now needs to be an identifier. So like also U32. And you'll see that if we now run cargo expand, this type alias actually gets included in the output because that's what the macro expands to. So any questions about like macro patterns before we move on this, we haven't actually written anything that's specific to VEC. This is more talking about what macro rules even does. When you say some tactically valid, do you mean it is valid Rust grammar or just valid Rust tokens? It has to be valid grammar. So this is why when I tried to use a single arrow here, it told me that I'm not allowed to have a type token followed by arrow. And this is basically because the Rust grammar does not allow that particular pattern. And there might be good reasons for this, right? It might be that that introduces some kind of parsing ambiguity. And that's why that particular syntax is not allowed.  So you'll see the the output is actually pretty helpful. It's saying allowed here are the following identifiers. And so you can choose what you want here. And if I hear as might actually make a lot of sense, right? And you see the moment I changed it to ask, this invocation is no longer valid, because it doesn't match what the macro pattern says it was expecting. And you could think that like, if if this was a macro that was named like type def, then this is a pretty reasonable syntax for it to use. Another thing you should be aware of for macros is that identifiers you define inside the macro are sort of exist in a separate universe from everything outside the macro. So for example, if I if I did not take an identifier here, let's do let's say there's sort of a default version of this, where this is just going to be also you 32. So there's one version that does this may have to do that. That's a bad example. Let's do this. So this in and of itself works. And if we run cargo expand, you see that it expands to type also you 32 is equal to you 32. But if I write something out here, there's going to be another you 32 is equal to also you 32. Maybe this does not apply for types, actually. Let me see if I can come up with a better example. Let's have a foo function here, which calls our AVEC with, let's have this instead just say, let x is equal to 42. So this is not going to take any arguments. So I'm going to use this macro and then I'm going to say like x plus one. If I try to compile this cargo check, you'll see that it says cannot find value x in the scope. Right. Even though this macro defines x. And the reason for this is the identifiers in in macro world are just completely distinct from the variables outside of macro world. So if I up here said, let x, let me X is 42. And the macro tried to say x plus equals one, this also would not work. Because the macro does not, it doesn't, it's not even able to name x in that scope. And this is where identifiers come in. So we can take like an x, which is an ident and then use that here. And then think of this as like passing the name into this other universe. So now we're the exit here is this x the name out here. And so this lets us cross that boundary. But you can't otherwise cross that boundary without explicitly opting into it. And this is one of the ways in which Rust macros are hygienic. They're not allowed to access things outside of their own scope. Identifiers in particular. Great. All right. Hygiene is not actually going to matter for us in this particular instance. But let's do sort of test different development here again, right? We want empty back is going to be the most obvious test we want where let x is going to be this. And then we want to assert that, I guess, assert that x is empty. And we with this is trivially true for the vector that comes from the standard library, we want to be able to use our own vector here. So let's start with no patterns. Right. What happens if there are no patterns? Well, it's now telling us that our macro rules is invalid. Right. It's basically saying I was expecting to see a pattern here, but there is no pattern. Arguably, this error message is really bad and it should tell us missing pattern. Instead, what it's really telling us is that it's missing a token in macro arguments. And the reason for this is macro rules itself is a macro. And it is expecting that between the open and curly open and close curly bracket, there's going to be patterns. So let's define one, right. So the for the empty vector, this should be really straightforward. This should just be back new. Right. We also here want to macro use and macro export this macro export, because we want anyone who uses our library to be able to call this macro macros by default or think of this sort of like pub for a macro without this, the macro will not be callable from any other library that depends on our crate. And if we run cargo cargo test here, you see that this test passes. And if we run cargo expand, it should be pretty obvious why that is cargo span test. That's a good question. Can I make it expand my tests without unis tests? So very good question. Maybe not. Maybe if I do tests. That's annoying. Fine. Let's just do this then expand. That's also fine. So you'll see that this function gets expanded to just back new, which is what we expected, right? That's how we define our macro. Great, very straightforward. Any questions about why this example works? So this is obviously a simple macro example, it takes no arguments and just produces a static value. But it's useful just to see that this does the right thing. What about ownership in the case of the dollar x example? Yes, in the dollar x example we had before, when you pass in the identifier, when you pass something to a macro, when you pass an identifier to a macro, you're just passing sort of access to the name. You're not passing ownership. It's not like calling a function. You're not moving that thing. You're just giving the name away to the macro. And then what matters for the purposes of ownership is what does the macro, the code that the macro expands to do with that name. It might move it, but it might also not. Try to expand lib tests. I don't think that's going to do it. Oh, nice. That does work. Great. Good call. Yeah, so this, this expands to what we expected. And it seems like this is relatively straightforward for people. All right, so this case is straightforward. Let's now look at something that's a little bit more complicated, right? So what we want here is one that has not an empty back, but like single. So this is going to be 42, we want to assert that it's not empty. And we want to assert, in fact, we want to assert that the length is one. And we want to assert that X zero is 42. And we immediately see that it's basically telling us this macro was not expecting anything. That's because we don't have a pattern that covers that. So let's add something like that. So we're going to add, it's going to take an E, let's just call it, this is just a variable name, right? I guess we could call it element if we want to be fancy. And it's going to be of type expression. So expression is anything that is an expression in Rust. And this is almost everything in the language that you can write inside of a function. Think of it as anything that you could terminate with a semicolon, roughly. And what is this going to expand to? Well, v's, I guess, mute. And we're obviously not allowed to use the vec macro in here, that would be cheating. We're going to say v's is vec new. And then we're going to do v's.push element. And then we're going to expand it to v. So we construct a new vector, we push the one element we got. And this is just going to replace whatever the expression in here was. So this is going to expand to vs.push42. And then we're going to give back vs. But you see that it's complaining here, right? If we run cargo check, cargo test, it says macro expansion ignores token vs and any following. The use of Avec is likely invalid in expression context. It says let expressions in this position are experimental. It's clearly not very happy. And the reason for this is that what we've told Rust to do here is we've told it that this expression expands to these three statements. And that's not something you can do in Rust. It wouldn't, you don't even have a way to write what this expands to in the language. Let's see what cargo expand gives us might not even give us anything reasonable. Yeah, you see here, it expands to just vec new and then nothing more, but includes like the let here. This is clearly not okay, right? Like, you can't have equals let mute vs that makes no sense. Really, what we meant to do here was we meant for this to be a block. And inside that block, we can declare variables and write other expressions. And then the final value in the block is what gets returned. And so you this is why in many macros, you'll see this sort of double curly bracket. And it's because the outer curly bracket is what the macro rules syntax require us to do. It just to say this is what this is the chunk of things that something expands to. And the reason it has to do that is because you might want to write you might want one call to a macro to expand to many items, for example, like multiple functions or modules, in which case, you don't want it to be a block. So you don't want it to default to produce a block, but you need the delimiter to be able to say all of the stuff in here. In this particular case, we do want it to expand to a block. And that's what this additional curly bracket is. Okay, so let's take questions on that before we move on. This roughly makes sense why we need this extra extra curly bracket. I can use a macro call inside of the macro, you can. There are some sort of weird rules around there. But in general, you can. How is macro rules, the macro rules macro implemented. So the macro rules macro is as far as I'm aware, not a normal macro. It is basically a procedural macro, sort of. And, and you can sort of see this immediately, right? We don't have a way with the macro rules syntax to have this representation. We don't have a way to say that we want an identifier followed by curly brackets, because all of the all of the macros we define using the syntax turn into something that you have to call with delimiters immediately. So we couldn't even write this syntax using macro rules itself, and you need to drop to something like proc macros. Should the no argument version of the macro use double braces, or doesn't it matter is that this version actually doesn't need the double brace. And the reason for that is this evaluates to a valid expression. You can think of it this way without the curly bracket, what's between here is not a valid expression. It is in fact a sequence of expressions. And that's why you couldn't use it in expression context, basically where the rust compiler is expecting expression, you can't put all these three things, but you can put this. In some sense, you can think of it as the, we're expecting the output type of this macro to be an expression, because we're expecting it to be used in expression context, like the caller is going to place it where an expression would normally go. And this conforms to that, this is an expression, but these three lines are not one expression. And so therefore would not work in that context. Whereas with a block, this becomes an expression. Are macros v2 still coming? Yeah. So there's a proposal for a new version of macro rules. And I think the idea is that it's just going to be called macro or macros, that is going to allow you to, it's going to be a more powerful version with slightly different guarantees about things like hygiene, and that it's going to work a little bit better with the module system and with use statements and stuff than the existing macro rules.  So it's going to be a new macro basically for defining macros, declarative macros as these are called. How does the square brackets work in the instantiation? They're not referenced in the macro definition. Yeah. So this is what we got out earlier that when you define macro rules, the delimiter is just freely chosen by the user between square brackets, round brackets, and curly brackets. And you can't, as the definer of the macro, you can't impose one or the other on the caller. The caller gets to choose. And all of them are valid and have the same meaning. The right hand side of the macro can be written with parentheses. Yeah. So you can actually do this here too. The fact that you can choose which delimiter to use here, you can do the same thing here. If I recall correctly. Yeah. So you can replace actually Rust format is going to undo this change for me. But it's valid to use round parentheses here. And I think it's even valid to use square parentheses. Basically any choice you can use here, you can also use for this outer block. But basically every macro I've ever seen uses the curly brackets for this. And as you as you may also have seen, if I save this file, Rust format turns it back into curly brackets. Can you have access to the custom compile time stuff like access some file or something like with proc macros? Proc macros are well, proc macros let you basically write a Rust program. Whereas macro rules does not let you do that. It's entirely declarative. All you can do is basically do substitution between one, one val syntactically valid Rust syntax tree to a valid Rust program or Rust syntax tree. Can reflection be used to return an expression? I'm not sure what you mean by that. But macros do not have access to things like type information. Well, declarative macros don't, they really are just substitution. If you want more access to like introspection, I think even proc macros don't really give you that. But certainly not in declarative macros. Can proc macros take identifiers before the argument block? No, so this is what we mentioned earlier that with declarative macros like this, oh, sorry, can proc macros. Yeah. With proc macros, you can basically write, you yourself define the input syntax and you're not constrained to the same syntax requirements that macro rules has. The limiter ambiguity doesn't seem rusty. It's a little weird. I'm not sure whether the like macro v2 stuff is going to have that. All right. I think that's most of the questions. Let's keep going. Can you quickly describe the difference between the types of macros? So declarative macros are what we see here.  Proc macros are programs that take Rust syntax streams as input or token streams as input and produce a different token stream to replace it with. So they are much more expressive, but also more complicated to write.  And so that's another way in which proc macros is special, which is something you can't do with declarative macros. All right. So let's now go ahead and try something real fancy. We're going to go with double. So now we have 42 and 43. And we're going to assert that the length is two and that both of them end up. So here again, we get an error saying, well, no rule expected comma, like you're not allowed to have a comma here. So I can't resolve this call, essentially. And the reason, of course, is because we've said in our pattern that we're only going to accept one expression and 42 comma 43 is not an expression. It is multiple expressions. It's at least not one expression. So you can imagine that like we could go this route. Oh, that's not what I meant to do. We could go this route, right? And say it's going to be e one. And then we're going to do e two. And this is going to push e one, and then push e two, right? And this will work if we try to run the cargo test. It's fine. But obviously, this is not going to scale. We want our vector to work the our VEC macro a VEC macro to work for an arbitrary number of elements and produce an appropriate vector. And so this is why the pattern syntax allows for repetition.  I think you can also put a question mark like there, but I forget. And what this means is I want, well, plus means one or more, this is sort of, you can think of regular expressions here, where plus means one or more star means zero or more repetitions of whatever is inside the parentheses separated by this token. So this is saying one or more comma separated things. That makes sense. So this will match one comma two, it will also match one comma two comma three, it will match one, because one is still a one or more longer lists separated by commas. But it will not support like one semicolon four, we could have this list be semicolon separate instead if we did this. Now this would be a semicolon separated list of one or more expressions. But in this case, this is the format we want, right? This is now valid syntax. And then inside the replacement part of your macro, you can use the same syntax. So dollar parentheses to give repetitions corresponding to the pattern. So here I can say, and this also expands to syntax. So inside here, I can say vs. push element. And then semicolon separated. Do that stupidly maybe. Oh, that syntax highlighting is just straight up wrong. I wonder why. Why is this not fine. So what this is saying is repeat what is inside these parentheses, the same number of times as the pattern that had element in it. So this is actually fairly complicated, the mapping between these pattern inputs and outputs. Because you can imagine that you have multiple of these, right? Like semicolon and then like X expert that is also like some comma plus. And Russ needs to know, am I repeating this many times or this many times? And the answer here is it looks for the pattern that has this variable in it. That's how many times is going to be repeated. And so here I'm saying, well, I want to repeat vs. push of element for each element that was in this pattern. And similarly here, I get to say sort of start to say I want to expand it that many times. So if I now run cargo test, this works just fine. And if I run cargo expand, you'll see that indeed in double X, the let X expands to a block, this is the double bracket, the curly bracket we add, that new and then push 42 push 43, and then returns vs. Alright, so that was a bunch of new syntax I threw at you. So let's talk through it. You can generate the macro with another macro rules, you actually have to be pretty careful with having macro rules generate macro rules, because the compiler sometimes gets confused. And we might actually see an example of that later in the stream. Yeah, you can have L you can use any single rust token here. So like, for example, this can be else, I think, not for expression. But if this was like type, for example, this item. Yeah, so this would be an else separated list of items. Just any single rust token is what you would use here. What's the meaning of the star on line eight? Well, oh, this star, this star is really just to say that this is a repetition. Arguably, it should be a plus to match the plus we add up here. But it's really just to say this is a repetition. And you can use the repeater in more than one place. So I can do this. And if we now look at the expand, you'll see that it did the expansion multiple times, right? This, this is still totally fine. And it's because each time it encounters this pattern of dollar parentheses inside of the expansion, it, it looks for which pattern I'm matching against, and then pulls out the variables every time. What happens to use both variables in a single or petition? That I think is just an error. Like, if we don't do this, and then I do like, let this, then if I now run cargo check cargo test, all right, I guess I have to give this, we're also gonna, we're gonna deal with this as well. So you'll see that this says metavenom. So you'll see that this says metavenom. We're gonna deal with this as well. So you'll see that this says meta variable x repeats zero times, but element repeats one time. And so it goes, I don't know what you want me to do here, because these are just different. I think maybe it allows it if they're the same, like if I did 42, and like foo here, and 42 here, and like foo comma bar, then I think it will actually allow the expansion. So it's just the only requirement is that if you use variables for multiple different repeating patterns, they have to repeat the same number of times. There are some questions here about VEC with capacity, we are getting, we're gonna get there. How do you define something like the format macro? The format macro is not macro rules. Macro rules is somewhat limited in what you can define. It, you can get away with a lot in macro rules, but like for some more elaborate things, you just need to drop to a proc macro. Plus this one or more star is zero or more, that's right. Any idea where that macro language got its inspiration from? It feels a little bit magic. I'm not sure actually where this sort of dollar parentheses came from. It's a little bit reminiscent of regular expressions, right? So a parentheses is a grouping in regular expressions. And then plus is one or more of the previous pattern, which if the parentheses is everything inside the parentheses, where the like dollar came from and where the comma or the separator came from, I'm not sure. Right. So let's now do another test. So single and double both work. That's great. Let's not do triple and everything that would be annoying. But one thing we want to do is like trailing. So for example, if I define a really long set of things here, right? So one, two, three, four, five, six, seven. I wonder how long this has to be before rust format goes. This should probably be on multiple lines. Fine. How about now? How about now? Great. So once you get to really long things like this, where things wrap, you very often just like want to be able to have a trailing comma, like this should be okay. Especially think of this as like if you have a list of things, these are the numbers are here pretty lush. If I do, this might be a better example. If this was like static stir, right? Then this should be okay. I don't really want to have to special case the last thing and can't have it have a trailing comma. I really want to allow this. But the current pattern doesn't allow that because the current pattern says it has to be comma separated. But there are no, if there's a trailing comma, it doesn't fit in the pattern because that would, then it would expect that it's a comma separated list of things. So there should be an expression following that comma. And the way, the way you get around this, there are a couple of ways you could just add a comma here, which makes it required. You could also put the comma here. This is saying, I want it to be separated by nothing, but each pattern should be followed by a comma. But then you require a trailing comma, which is not what we want either. So the way you actually do this is you add this pattern, which is kind of stupid, right? So this is saying, this is our normal pattern. And then following the normal pattern, we want to allow any number of commas. In some sense, you could think of like question mark is really the thing here, right? Of zero or one. And I think that's also legal. Great. It's just my highlighter doesn't pick it up. So this is saying zero or one of this pattern. And notice we're not using this pattern in the expansion at all. We're just saying that it's allowed to be there in the pattern. And the question mark is zero or one. So zero is also okay. So it's fine for there to not be a trailing comma. Do you actually need the comma before the plus, or is it just indicating that the user should use commas as separators? You do need it. If you don't have this here, then now it's it's expressing it's expecting a sequence of expressions that are not separated by anything. So this would make the invocation this, like notice there's no separator apart from just white space. So you could think of white space as being the default separator. But we specifically want the separator to be comma. It's not like the user chooses the separator. It is the separate there is no separator. Can you match a specific number like you can with regex braces? I don't think so. I don't think this is full regex. I think the only things you have are question mark plus and star. But the like look it up in the macro book, it might be but I'm not aware of it. Can you check repetitions length to print more intuitive error with two repetitions that are not the same length? That probably gives you decent errors here. One thing you'll find with declarative macros is that you sort of get what you get. Like you can't really give nice error messages. You can try to tweak your macros so the error messages happen to be nice. But if you really want to like provide a really powerful macro where things can go wrong in subtle ways, you really want to proc macro where you get better control over what went wrong and what code you emit and what errors are emitted. What are the general benefits for defining your macros? It's just really handy, right? Like one thing I use it a lot for is things like I want to generate. Here's a maybe trivial example, but imagine that I have some trait foo and doesn't have any methods or it has methods that make sense for any number type. In fact, how about we do this? Has max value and it does like max value. And this trait can obviously be implemented for like all the numeric types. And so I could write imple max value for U32 fn max value return self U32 max, right?  And then I do the same for you 64. And then I do the same for right, like, I can totally do this and just have lots of them. But macro rules is a really handy way to just do this quickly. Right? So I do a macro rules. And I can just call it imple. And it takes a, it takes a T, which is a type, and it generates this. I'll explain this in a second. And now I can just write like imple U32. Imple is probably not a good name here. Max imple I 32 U32 I 64 youth 64, etc. This doesn't work because that's interesting. Oh, right. This is also a stupidity, right? So now I don't have to write out that implementation multiple times because they all basically have the exact same pattern. And instead I can just define the pattern and then repeat that pattern for multiple times. So that's an example of where, where you would use macro rules.  And you just want to express the pattern rather than, than like, imagine that I wanted to change something in this for every implementation for an American type. Well, if I wanted to do that, and I had a copy of the implementation for every type, that would be really annoying to do. Yeah, derived logic is written with proc macros. What determines if a macro is called with parentheses or square bracket, you choose as the caller every time. Okay, so let's get back to our macro. This now works the trailing comma just works. So this seems pretty great, right? We now have a macro that just sort of does the right thing. But let's do this the other way around first. But the other pattern that we want to support is one that takes an element, a semicolon, and then a count, right? And there are a couple of ways we can do this, right? So the, the simplest one is like four in zero to count vs.push. Let's do element. Let's see how that works. So this is the semantics of having like a thing like that thing, semicolon count is that you get a vector that has that many of that thing. And this like this works. If I do clone two, and I say 42, two, then this test will probably pass just fine. Unfortunately, there's a couple of things wrong with this. The first of these is remember that when the macro expands, it basically does substitution, right? It takes this expression places it there. And that's fine if that expression is like a constant. But it's not fine as if this is a more complicated expression. So imagine, for example, that we have non literal. Let's imagine that this is some 42. And this is going to be why dot take dot unwrap. So this is basically the same thing as before, right? We have a thing here and we want two of it. And that thing is going to be what was inside the sum. So it should just work. But you'll see that this actually panics, this doesn't work and the unwrap fails. And the reason for that should be clear if we do our cargo expand. Because that test expands to for each in this loop, do y dot take. And obviously, only the first take is going to succeed. The second time we take the value there is none. And so this will just not work. And so this is something you need to be aware of when you do macro expanses is that it really does substitution. In our particular case, what we want to do here, right, is really something like let x is element. And then we want to do x dot clone. That's really what we want to say here instead. That way, the expression that gets passed only is only evaluated a single time. And then we remember its result. And then we clone it for each time we push. And this will in fact work. All right. That was also a lot. So let's do questions on that. Let's see, are macros in Rust the answer of people who want object oriented programming like inheritance? No, I don't think you want macros for object orientation at all. Do you find macro rules readable? No. So macro rules, once they go beyond sort of simple ones, I think these are decently readable. I think once you get to the more complicated ones, they can become a bit of an eyesore. And you really want to move to a proc macro. The biggest downside of a proc macro is that it it's a relatively heavy weight thing, because now you need to be able to sort of parse and interpret well, interpret not really parse rust token streams. And so you need a dependency on like sin and maybe quote. And and proc macros add an additional compilation step. So it makes using users of your macro now need to spend a lot more cycles on compiling your thing as opposed to macro rules, which are relatively lightweight. Let's see. Can you have a test where count is not a valid expression? Yeah, so if we do a test that like invalid count, where this is like, I guess, foo, right? Let's make it a little simpler. So this will not compile because it will say it expected an integer found a string. And the way this actually works behind the scenes is that um, rust when compiling this, any error that gets generated by the macro, like in this case, there's an error that is generated for this part of the macro, which is like expected integer found string, any error that gets produced for the code that the macro generates gets assigned to in the output, the output error messages gets assigned to the corresponding place in the macro input, right? So the error is about count. And so rust figures out that the count came from here and then points that error at that location in the output, which is why this output error actually ends up being pretty nice. It points to the count and says expected integer found string, even though this error really comes from the location much further up here, that's where that error originated. And yeah, the other problem with the previous approach where we had element here is that if element was pretty expensive, it would end up being called n times, whereas now it only gets called once and then you clone. Will a clone call on a literal like 42.clone be optimized out by the compiler? Yeah, usually. So 42 is an integer. It's an i32 usually, which implements copy. And the compiler is smart enough to recognize that for for if it knows that the type is copy, then it will just copy, which in the case of something that's as small as an integer, a copy is just like a register register move, usually. Can you use this newly introduced syntax to say compact arbitrarily nested for loops? I'm not sure I follow, but usually like macro rules are pretty restricted in what you can write, although you can write patterns here that like match a for loop if you wanted to. Great. All right, so let's move on a little bit. So there are a couple of downsides to the approach we're currently taking. I'm going to remove this because it doesn't compile. Although there, so I'll show you a little trick actually, which is in doc tests. So Rust doesn't have a way to say that a unit test should not compile. But there's a crate call like compile fail. I think it's compile fail that lets you write tests that are not supposed to compile. But one trick you can pull here is this. Doc tests can. So if you do this, well, I guess we'll do allow dead code. And now do cargo T, then you see that it actually runs that test when it runs the documentation tests, and it checks that it does in fact not compile. And so if we now change this to something that did compile, like 42, uh, ooh, am I missing something? Where in this? What happens? Oh, right. Let's do what do we call this? Vic Mac. Yeah. So now this compiles. And if I mark this as compile fail, then that test is no longer going to pass because it's supposed to fail, which means that I can now make this foo to make sure that that actually does not compile. Yeah, it's pretty cool. It's like a cheap way to get compile fail tests. There are a couple of reasons why this is you, you may not want to rely too much on this pattern, such as if it compiles for if it doesn't compile for a different reason than you wanted it not to compile, this won't quite do the right thing. But it's a heady pattern. All right. So are we done? Like now our macro does the same things in the standard library. In fact, let's switch over and see what Vic does, right? So Vic supports the pattern, element expression, semicolon and expression. Okay, we got that covered, right? That is this guy. It supports any, in fact, so this is how they express trailing commas is either x expression comma star or x expression in with a comma inside the pattern star. And that is also a valid way to express this. They are basically the same. The advantage of us doing it this way is that we can express it with a single pattern of the rather than multiple. And notice that they don't have this version of the pattern. They get rid of this entirely. And we could do the same thing by making this star instead of plus to say that this is like zero or more repetitions of elements. One reason that this is a little annoying is because we're going to get this warning, right? That the VS that we declare in the case where this list is empty does not need to be mutable. And so we actually want to, in that case, we would have to allow, put an allow on this. Just in the case where the input list is empty and we never push. And that works fine. So this just reduces the number of patterns. And it's going to make the documentation easier to read, right? Because now the documentation is only going to show the two patterns that we care about. Oh yeah, try build is another good crate to know for compile fail tests. Should panic is different. So should panic, it must still compile. It's just that when you run at panics, that's different from compile fail. Macro export is not always required. It's just that without macro export, you wouldn't be able to call this macro outside of this crate. Think of it as pub. All right, great. So there are a couple of things that are unfortunate about this. And the first of these is that we're calling new everywhere.    The I forget what the default capacity of a vector is, but it's probably somewhere around 16. You after you've, you push the first element and then it has to allocate a vector holding 16 elements, then it pushes 15 more. Then when it pushes the 17th element, it has to reallocate, copy all the elements into a vector that's twice as large to the 32. And then it pushes another 15 elements. Yeah, another 15 elements. And then when it pushes the 33rd element, it has to resize the vector again, and it keeps doing this over and over. Even though if we know that there are 1024 things, why don't we just allocate for 1024? That seems much more obvious. And of course, this is much easier to do for the case where the user gives in the count because we know what the count is, right? So here we can really just do with capacity count. Although here we got to be aware of the same thing we looked at earlier, which is now we're going to evaluate the count expression twice, which is probably not what the user expected. So we do let count is count. And then we use count here and count here. Okay, so now we have a much more efficient implementation of this sort of I want this many iterations, we can still do better though, which is push is a little stupid, because every time you do a push, we've gotten rid of the allocation part, but it still has to do like a pointer increment. And this sounds silly. But if we're like creating a vector of 1024 elements, almost like in a busy loop, then every instruction counts. And there's no reason to have to do the bounce check for every push, right? And the allocation check for every push, when we know that that's not necessary. And there is already a way for us to get around this, which is we can do VS dot extend. And then we can do like the standard iter repeat element take count. So this is saying the standard iter repeat is a really handy iterator method, which is just it yields clones of the element you give for as long as you take from the iterator. And then dot take is a method on iterator that says only take this many things. And so now extend knows that it's going to keep adding to this vector. In theory, we could do even better if this implemented exact size iterator, but we're going to not deal with that today, because it's a slightly more complicated topic. This because repeat plus count, repeat plus take, I think does not implement exact size. And you can think of here as if you try to extend a vector with something that you know the size of the iterator in advance, you can just sort of pre do all the bounce checking in advance, and you don't need to do it during the iteration. But this is pretty decent for a starting point. All right, so questions about that change first. Do you think the docs will be more readable with just that one pattern or with two slash three? I think fewer patterns are better, unless the patterns get extremely complicated as a result. Do you think I said he's 10 on the first allocation? Yeah, that makes sense. The argument still holds. Yeah, so standard data repeat has a bound that the type of the element implements clone. Because the compiler optimizes series of pushes using a with capacity call. It could I don't think it will, because that's a much more sophisticated optimization, right? That requires the compiler to know the semantics of that it needs to know that there's some relationship between new and push and with capacity. There's a much more sophisticated operation that required special treatment of VEC. So I don't think it'll do that. It would do that if we used VEC from iterator and managed to produce an iterator, the implemented exact size iterator. But that's a topic for a different day. Are you sure? Oh, yeah. So the macro, this is an important point, actually, the macro doesn't have any trade bounds, right? The macro doesn't say that element, the type of element needs to implement clone. Macros in general can't express that. Instead, what's going to happen is if you try to use something that isn't clone, then the compiler is still going to generate this code. And then just as if you had written this code with the given element in this position, the compiler would complain that that element is not that type does not implement clone, just so the macro expansion will generate that error. And we'll point at element because the compiler generated an error here, which is this variable. So I mean, we can try it, right? If I do a non clone, and let's say I have a Y that is a, I do a struct foo. And I say Y is a foo. And I want to construct a veck of foos that is of length two. If I do cargo check, it'll say up here that the trade foo implements clone is not satisfied. And it'll point me at foo. And it says it even points you at required by this bound and standard it'll repeat. And what's really happening behind the hood is it expands the macro. The macro generates this compile error for where we create the standard it'll repeat. And then that error gets sort of bubbled up by the macro expansion to point to where the type that didn't implement clone came from. It's a pretty sophisticated machinery, but the error output you get is pretty nice.  And other errors like we can't print foo because it doesn't implement debug. So hopefully that was illustrative. Capacity from plain pushing goes 01248. Yeah, so it actually does purely power of two expansion. So that actually means that it will do 10 allocations before it even gets to our 1024. It'll be really inefficient. Is that hygienic? What happens if the caller defined a mod STD in a mod iter? Yeah, so this is one of the things you have to be careful about with whenever you define macros is that the caller might have modules that override you here like STD might refer to a module in the caller scope that is not the standard library. There are a couple of ways you can work around this. This is one ways in which macros aren't entirely hygienic. You'll see things like create. So create refers to the crate where the macro was defined always, no matter how the caller brought that into scope. And then you can also do colon colon, which is this is a root level path. So STD must be a crate. Although they could still pull tricks like rename a crate. But if someone renames the crate STD, then they deserve the problems they get. But yeah, arguably it should be this. Why not vec resize? Oh yeah, vec resize would also work here. We do vec resize element count. And that will also work in theory. Did I do this backwards? Great, yeah. So that's another way to do it. And this is probably even more efficient because as you observe, this doesn't have to do the bounce checking. The reason why the standard library deals with the trailing comma as it does is because it disallows invocation with comma only. Yeah, that's interesting. So with our syntax above, this is legal, right? Just putting a single comma in there. With the version the standard library uses, that is not legal. So that's like an interesting observation. And the translation between the two is actually pretty straightforward, right? If we did this, and then we said this should turn into create a vec recursion limit, really, that seems false. Oh, I might have to define the other one first. The order might actually matter here. There we go. Yeah, basically, if there's not a trailing comma. So the issue we were seeing is that this ended up giving us infinite recursion. And the reason for that is if you have an expression that doesn't have a trailing comma, then, or rather, if you have something like this where there are a bunch of elements, then it's going to keep invoking this rule. That's not even true. I don't know why it has to be in that order. But let's not worry too much about that right now. That seems fine. Great. All right, so now we have something that works here. Obviously, this with capacity trick is really nice. We can even make this new now if we wanted to, because resize is going to take care of making this a single allocation. So why not we do this, make this count, because now it's only used once, and this will still work fine. Great. So now we have a relatively simple expansion that's still efficient for our repeated example. We still have this problem up here, right, of this really should be with capacity. All right, so let's try to do that with capacity. But what do we put here? What is the count? So there isn't really a weight at least currently in macro syntax to say how many items are there here. But this is where we can get tricky with macros. And this is actually a pattern that is covered in the in the little book of Rust macros, which is how to count things. And it turns out there are many ways to count things that are really sneaky. So before I go to that, because that's sort of its own trick, let's see if there are questions about any of the things we've covered so far. This is going to be its own like self contained bit. Yeah. So someone wants us to have a clone bound earlier. So I mean, you could do something that you could do something like x is element and then then like test C clone takes a reference to see does nothing with it. And then we're going to do test x. Right. So this is one way to sort of test in your macro that something implements implements clone if you wanted to. But generally, when you write macros, you just sort of write them the way the code will be written and the hopefully the macro rules error propagation should take care of propagating any bounds that the user needs to know about. You can't really document them in the in the pattern for the macro. Usually, if there's something that's not obvious, you would put it in the documentation for the macro. Right. So you can write here documentation here that will appear on the macro itself. Can you access data from the calling scope from within the macro? No, not generally. This is something we mentioned earlier in the stream. Okay, so what do we do here? How do we get the count? Can you have a version of the macro the defaults to evaluate element many times if element doesn't implement clone? Yeah, I mean, you could you could totally have one that that that does the element that does like vs. push element to reevaluate expression this many times. I think that's generally not what you would want. Like clone is probably just better. Like if you can reevaluate the expression that many times, then why isn't the thing you return clone? If that makes sense. But it doesn't have to be. All right. If there aren't other questions about that, let's try to figure out how we can produce a value here. Like how can we do let count is something in such a way as let's make this the new fancy to do macro in such a way that we can do with capacity. How can we do this? Well, the first thing we're going to need is this obviously sort of has to be a macro, right? Like there's no function you can give a variadic number of elements to really. So what we're going to do here is probably invoke a macro of some kind.  I'm going to propose to you that we do this with me here for a second. All right. So what do you think this will do? See the trick here, right? Like this creates an array that has all the elements in it. And we call length on that array that's going to give us the number of things in element. Now this particular expansion doesn't actually work because we're going to be consuming element twice, we're going to be evaluating every expression multiple times. We've already talked about how that's not okay. Right? So this clearly won't work. But is there some variant of this that we could use? Well, what if for each element rather than put element here, we put something else, because we don't actually care about that the things that get put inside of this array, all we care about is that it has the right length. So we're going to do is actually define a sort of private variant of this macro. And you'll see why I define this as a new pattern rather than a nested macro in a second. There are a couple of other ways to do this. In fact, we can do it. I want to do is a pattern for now and we can move it out eventually. So I'm going to use ampersand in this pattern just to sort of indicate that you shouldn't be calling this. Because no one's going to be writing like that followed by ampersand count, and then something else, it's clearly meant for internal use. There were some ways to improve this later on. And it is going to take element, it's going to be expression. And we don't know what it's going to expand to yet. Right. But it's sort of going to be something like this. If it could be that, that'd be great. But that doesn't actually work. And then in here, we're going to do create avoc of element. Right. So that makes sense. Like, this is going to be count. So this is going to invoke the avoc macro itself, but it's going to end up hitting the pattern that's down here. And I guess I put a, let's make this a semicolon. So that's going to end up expanding to whatever this expands to. We still have this problem. This is going to repeat element multiple times. So the trick we're going to pull is you can define a macro. So what happens actually, if we just like made this unit, that'd be great, right? If that just worked. But what happens if we do this? Well, the compiler tells us we attempted to repeat an expression containing no syntax variables matched as repeating at this depth. What does that mean? Well, remember how when we talked about these special patterns, we said that rust figures out how many times to repeat this particular block by looking at which variables we use inside of it. So here we're using element and therefore we look at the pattern that initially took element and look at how many times that repeats. Well, this pattern or this expression rather, this repetition doesn't name any variable. So rust doesn't know how many times to repeat. And one way to exemplify this is imagine that this was really the definition, right? How many times should this unit be repeated this many times or this many times rust does not know. And this is why it requires us to use the variable in there. But we just said we don't want to use the variable. So can we do any better? Well, it turns out we can. So let's define another variant here, which is going to be substitute. And substitute is not going to take a repetition. It is just going to take an expression. And then what's it going to return is unit. So notice that this takes this doesn't actually use its argument, but it takes one. Right. And then it just returns unit. And now we're going to expand this with AVEC itself with substitution. Does this make sense? Right. So now rust knows how many times to repeat this because we're using element in there. So it knows to look at this pattern. But then when it expands this macro for each element, that macro ends up using just unit. It does not actually put this expression anywhere, which means that that expression won't be called multiple times, which is what our worry was. And so now we end up with this many units inside of this. And then we want the length. And that is a totally valid expression. And it doesn't actually use the expressions from element anywhere. And so when this expands to is then just the length of some really long slice, which is in fact, even known at compile time. Right. Let's see if this actually works. No rules expected the token. Yeah, that's because we probably need these. Right. So now it says no rules expected this token. That should be subst. So now it's saying cannot infer type of Len. So this is kind of interesting. We have this, this ends up producing an array. But it the type does not give the length of the array. And so it can't actually infer which link method are we talking about. And so we need to be this is where things get like, this is where sometimes you see really nasty hacks in macros. So we're actually going to do this. Sorry, this needs to be a I'll explain the syntax in a second.  But it will work. Yeah. Okay. So what this is doing is saying, take this array, take a reference to it, and then call the implementation of Len for slices of units. So notice slice not array and call it. And because arrays implement as ref slice, we're allowed to call any method that exists on a slice on the array by just you calling the as ref trade. So this ends up calling slice Len, the slice method Len on this array turned into a slice. This is like mind blowing weird expansion. So let's actually look at what this expands to. So let's look at our double in the very early days. So our double function, if you recall from where we define it, right, double is just a vector with two elements. Let's see what that expands to. So it expands to VEC with capacity, the length trick, and then array with two units in it, right? So this whole expression is actually evaluatable at compile time by the compiler because this is because I hate that it jumps down like that. Because where's my double here? Because this is a two element array, the compiler knows that a compile time, it also knows that a two element arrays length is evaluable at compile time to be two. So this will actually be a compile time with capacity to even if it wasn't, though, this will still evaluate to the right expression to which is the real length. And so this will not allocate anymore. All right. So I just threw through a really fancy trick at you. Let's see. I also want to point out there are actually multiple different ways to pull this particular counting trick. This is the recommended one because it works for arrays of any length. But there are some other ones that are pretty nasty. Someone wrote, oh, gross. It is gross. Gross. It is really gross. But it works. It does the right thing. This is turning to be nasty. It is. But it is pretty sneaky. And specifically what this gets really nice is that unit is a zero size type. So this doesn't actually take any size on the stack. We're not actually using any memory for this. This is entirely a computation that happens without that. In fact, you could get really fancy here and use size of. There are some tricks around size of that. But then you end up allocating it. In fact, here, let me pull this up in the macro book. If you look down here at counting. So you'll see this is sort of the replacement trick that we used.  So it ends up expanding to zero plus one plus one plus one plus one. And it turns out you will crash to the compiler if you do enough of these. You can do recursion. You can do like a bunch of sneaky things like this by counting the number of tokens. And you can like batch them. But ultimately, like, you really just want to do a slight length. This has been tested up to 10,000 tokens. But you can't do this in Rust 1.2. But apart from that, it cannot be used to produce a constant expression. Yeah. So okay, so this produces a non constant expression. That's too bad. There are also versions to do that. Like this thing, you can extract the enum counter to get the number of items. So actually, I lied, then this is not the version we use is not constant. But it is sort of the nice one. This one is this one does work at compile time, but don't do it. All right. See, yeah, when it gets too nasty, like you could write this as a procedural macro, right? You shouldn't need to pull this trick. But it is cool that it's possible. And one of the reasons I wanted to include this trick is because it tells you a lot about how macros work, even though you might not necessarily want to do this yourself. There's not a predefined count macro. No, not currently. And part of it is because it doesn't know what you want to count, right? You want to count the number of tokens. Do you want to count the number of like the number of characters in the syntax tree? Do you want to count the number of the number of items, right? And so it's sort of hard. You would need like generic macros in some sense to be able to say what should the type of the thing that we're counting be. Oh, sweet. Okay, great. So Len is const for slices. Great. So then what I said was true. Is there some way to test verify that the length is known as compile time? Yeah, we can even do that just to finish up here. So we can do, I think it will let me do this, that count is const. Yep. So it is in fact constant. Great. Why not use ints like zeros for vet capacity? You don't want to use a zero because zero, then it's going to keep reallocating. Do the count and subs patterns show up in the docs?  I forget whether you can do doc hidden here. But the other way to get around this is sort of what I was going to say wrapping up is you can do this, move these guys out, make this macro export, but also doc hidden. And then here have this instead be a vet be count. So now, in fact, if we try to do this, see if this works right. It showed up on the wrong monitor, because of course it did. That's not at all what I meant to do. Sorry about the light. But you see this only shows one macro, and it only has the things that we expect. So this is one way in which you can hide those sort of nasty patterns. At this point, you don't need the prefixes either. Why does it matter if we use the parentheses when the length is known to compile time? So if we didn't use zero size types here, then this would still be stack allocated. It might be optimized out by LVM, but you would still need this array, the array we construct here to be somewhere in order to be counted. Whereas given that it's a zero size type, it doesn't even need to be stored anywhere. Even though the compiler can recognize that this array is no longer used at runtime and optimize it away, but by making it zero size, we sort of guaranteed that it won't appear in memory anywhere. And also note that this const check is really just for our own sake. It's not actually used for anything. You could do C and then make this be C. No, that's not what I meant to do. And that would also work just fine. Nice. All right. I think that's everything I wanted to talk about macros. We now have an equivalent of the standard library VEC macro in a relatively performant version of that that we implemented ourselves. So it's pretty cool. We're still missing a bunch of documentation and stuff, of course, but I feel like that's sort of out of the scope of these crust of rust episodes. The thing I didn't quite get to, which I consider doing, but I don't think it's terribly important is you could imagine extending this to instead of this defining a vector, it defines like a hash map or a B tree map or a set of some kind where you just change this to instead of being elements be like key arrow value. And then you repeat that with inserts instead of pushes. So that's something you can do. And there's a crate called maplit that basically does that. And I highly recommend you try it out to sort of as practice for yourself. I'm going to end the explanation there, but I'm going to take some last minute questions if there are any, and then we'll wrap up for this time. Let's see. No, it looks like people are generally following. If you're looking for an exercise, I guess you some people are wondering whether like, how can I practice this? Try expanding or extending what we have here to work for a hash map, for example, because it should be a fairly straightforward change to the syntax, but it will require you to like, check your understanding of these concepts. Can you show us the standard library version? Ooh, that's a good question. The standard library source version. Who knows what that does? It uses from Ellen. So we could have done that too. This is oh, I see what they do. This is a little weird. I don't know why they choose to do it that way. So this is going to create a box slice. So it creates a it basically creates an array that is going to use the box keyword to make that array be on the heap. And then there's a conversion from a box slice to a VEC. But I feel like that will end up having to move. VEC is two words and a pointer. And box is one word and a pointer. So I'm not sure why this conversion is free. Oh, right, because the words you add on the stack. Okay, so the answer to this is basically the representation of a vector, right, is you have on the stack, you have the length, the capacity and a pointer to the data, which is on the heap, right. And a box is just a pointer to something that's on the heap. Well, what this does is create an array of all the elements. And the box keyword says do that on the heap. This is something that I think you need a nightly yeah, you need this nightly feature to get it to work. And what that gives you is a boxed array, right? So this is an array on the heap. And then it uses here, it's saying treat that as a boxed slice instead of a boxed array, and then call into VEC. And the reason for this is if you have a sequence of things on the heap, like you have that pointer, you can trivially construct a vector, right, because you just construct something that has that length, that capacity, you just sort of count them. And that pointer, and it will just work. And this allows them to get away with not doing all the tricks that we did. That makes stuff like you ate VEC be optimized to memset. Maybe. Yeah, although we don't need to touch the stack either. Nice. All right, I think we're gonna wrap everything up there. I hope that was useful. Thanks everyone for watching. And I will catch you on whatever the next crust of rust is. I'll make sure to announce it on Twitter. And if you have ideas for additional sort of small concepts, things that you think have relatively self contained, real code, we could write, then reach out and let me know. Sweet. Enjoy everyone. I'll see you next time.


Do I actually have products that I feel like have truly been game changers, not just things that we review and we say, oh, this is great. No, these are game-changing products which have really made a difference in my skincare hair care and especially my makeup, and I'm bringing those to you in a video today. Hello, my friends. Not only am I bringing you some amazing products that I truly feel have made a huge, huge impact on my beauty routine, but I'm also bringing you a collab with my dear friend Kimberly from Pretty Over 50. And I know that most of you already know Kimberly. She is just such a warm, comforting soul to watch. She's a beautiful woman and I have just appreciated her so much. Her and I got together on a collab not too long ago, but Kimberly has come onto the scene on YouTube and skyrocketed. And I am so proud of her for what she has done. She has just enveloped the mature beauty community and she has just done wonders. Her videos are very informative. They're very thought out and they're very, very helpful to the woman that is over 50. I appreciate her so much. I love watching her. I know that if you haven't watched her, you're going to love watching her. I want you to please go over and click on her video that is the same title as this one. Subscribe if you do enjoy her content. Don't forget to ring that little bell because you'll be notified of any other content that she does upload and save goes if you are visiting me from her channel. I would love to have you subscribe. Be a part of the little corner of YouTube we have here. And thank you so much for watching this video. Oh, I have kind of a well rounded bunch of products here. I have some skincare, one hair care item, and then the rest is makeup. So I want to start with the skincare that I have. I have, first of all, this from Good Molecules. If you've been around my channel for very long, you know that I love the niacinamide brightening toner. It's one of my holy grail skincare items that I have had for years now since it came out and I will never be without it. Good Molecules is a fantastic line if you've been thinking about trying it. Let me just tell you this says that it is formulated with niacinamide, vitamin C, Arbutin, or Arbutin, I don't know how you say it, and licorice root extract to brighten and improve the appearance of enlarged pores. So niacinamide is that great sweep that goes in there into our pores and it sweeps out all the old dead skin cells, the dirt, everything that's in there that can clog our pores or make our pores even look larger because they have gunk inside of them and are stretched out. So niacinamide goes in there and does that beautiful sweep. But I like to tell everybody about this product is that when I did get it when it very first came out, I was using it and I was looking at it and I was going, is this for real? Because it was like, I think maybe six or seven days later. It wasn't quite a week and I started to look at my skin, especially on my pores and I'm like, are my pores becoming more refined? Or am I just crazy? And I knew that this was the only product that I had changed in my skincare. By week three, I was noticing my skin was brighter. Everything about my skin had changed just a bit, just enough to tweak it and make it look better. And so this is a product that I use every single morning. I am never without it. I will use a glycolic toner like three days a week before that, let it completely dry down and then this one goes on next. And the ingredients in it, the Arbutin, the licorice root, the vitamin C, all of those are super brightening. So if you have trouble with dull skin, I think you're going to love this. And for me, it was an immediate game changer. It wasn't like normally with skincare, you have to wait like six weeks or whatever to see. I noticed a change in my skin very, very quickly and I love that stuff and I'll never, ever be without it. The next product I know I have told you guys about so many times. That is the Sukoyaka Suhara eye cream and I know that there are a lot of people out there, dermatologists out there, say that you don't need a separate eye cream. This isn't a serum eye cream that's going to take care of wrinkles or your darkness or anything like that. What this is, is an uber hydrator. It is such an intense hydrator that it completely changed everything about my eye makeup, especially my concealer. The main ingredient in here is urea and I get asked this question a lot so I clarify every single time. Yes, urea is a component in urine, human urine, animal urine. However, this is not urine. This is created in a lab to mimic what that does, which is super softening and urea also has the property of doing a tiny, tiny sloughing or a tiny, tiny exfoliating. It's not irritating. It's very, very soothing. And I will find that if I have a time when the tretinoin or any extra acids or anything has really kind of traveled up and hurt my eyes, I reach for all my urea products because they're so very calming on my skin. I will show you the texture of this. It is not super thick, but it's not super thin. So it's like in between a lotion and a cream and it spreads over your eyes. It sinks in very quickly, but you will notice that your skin is so plumped up and so hydrated and it is a hydrator that lasts for hours and hours. And I at the end of the day don't feel like I have any creepiness from drying out or anything like that. And I will tout this until I no longer have a channel and then I'll tout it to whoever else can hear. I love this stuff. If you have been somebody that struggles with those horrible wrinkles around your eyes, give it a try. It really does make a difference to hydrate any area where you have super dehydration and most of us over 40 notice that dehydration, especially if you get past menopause. It becomes a true, true problem. Next product I want to show you is also skincare. However, it's for the body and I know that a lot. Well, I'll get into that just a second. Okay. It's the Alpha skincare renewal body lotion. This is 12% glycolic acid AHA. I adore this lotion, especially for my alligator legs that get so very dry. My heels that can get cracked at times from being so dry and my hands. I want to put a little bit of this on my hands. Now I did just put kind of a hydrator on that hand. So this hand right here is a little bit dehydrated, but I'm going to put this on. Now, the one thing that everybody does say when they use this lotion is it's hard to get out. It's thicker and because I'm a dry skin person, I don't mind that so much. So it is a little bit thicker. It's a little bit harder to get out, but I find that this lotion is so very thick and lovely on my skin and my hands have never looked better than when I did start to use this. I use it morning and night. The other thing I noticed as soon as I started using it is that my cuticles started to heal. I had very dry splitting kind of bleeding cuticles and they started to dry as well and my hands. I feel like they have never been in better shape and I do attribute that to this lotion. It will feel a little bit tacky, but I have noticed in the past that when I did use lotions that said that they were super healing or super hydrating that I felt like it was just dry within minutes. You won't feel that with this. It stays very tacky. One thing that I will tell you is that if you do get hot when you go to bed or if you get very warm when you go to bed and you do sweat a lot, try to avoid putting this on right before you go to bed on like your feet or your legs because I did that one night and I felt like I was sticking to the sheets all night long because I do sweat a little bit at night myself and so I put this on right after I get out of the shower and just let it sink in for an hour or so and then you can go to bed or you know, if you're doing your daily routine and go about whatever you need to. It's wonderful. It's so good for hair very quickly. I'll touch on this because I have touched on it many times is the L'Oreal Paris L Vive 8 Wonder Water 8 second Wonder Water before I cut my hair. My hair was super duper dry, very, very dry and it's limp and it's fine and it has a hard time holding a curl. All the regular conditioners that I was using would just make it feel very limp still and I didn't feel like it was very soft. I still felt like it was dry. This is very liquidy almost like a micellar water and you put a tiny bit of this on. It kind of guides you through how much to use each use. You put a tiny bit of it, work it through your hair and within seconds it really does make your hair feel silky in the shower.  The other thing is the shine on it is great and it just makes my hair feel conditioned and it just it doesn't ever feel like my hair is drying out when I use this. I love this. This is my second bottle and I definitely wanted to tell you about it because I felt like it's game-changing for my hair. All right three products left. Let's move into makeup. For me the Max Prep and Prime Fix Plus has been a staple in my makeup routine for years. I used to use it as a primer water that I would spritz across my face for a little bit extra hydration but as the years have gone by what I finally decided to do with it is actually use it as a step in my foundation where I would use it to thin out foundations just a little bit and make them look a little bit better. Let me tell you what I'm talking about. Don't take the Fix Plus. I'll take a brush. This isn't the brush that I use for my foundation but if I was going to use a brush for my foundation I would spray that and then go in and spread my foundation and buff my foundation around and I'll do it on one side. I'll spray one more time and then I'll do it on the other side and the reason that I do that is not only does it give a little added hydration to that foundation but it just goes across your face like a dream and I love that effect. You can also do it on a dampened beauty sponge. That's actually how I started to do it and if I have a foundation that is really thick I will do a beauty sponge instead of a brush because it does thin it out even more kind of soaks up into the beauty sponge a little bit but spraying this on the sponge will make your skin look absolutely flawless. I know that at the drug store there are a few priming sprays but I feel like none of them come close to this and right now I know that there are duos on sale for the holidays where you can buy two of these about for the price of one just a few dollars more so I'll make sure that I link that really good deal for you below. If you're interested in this you can also get it in a little trial size if you are afraid that you might not like it. I love that too so this definitely was a game changer for my foundation. I just felt like my foundation just blended smooth and flawlessly across my face when I started doing that loved it so much taking with foundation for just a second. The next product is the LA girl pro color foundation mixing pigment I have one in blue and I have one in white and I will tell you that these have made a huge game-changing difference in my foundation game because I would buy foundations and so many times foundations would be too orange too yellow too dark whatever the case might be and I would have to either return them which I feel like is a waste for the company or I would have to throw them away which is a huge waste for me right so I really have enjoyed having this the blue one will take any orange or yellow foundation and really tone that color down make it to where you can wear it no matter if you're neutral or if you're a cool toned gal it works like a dream and then the white again like I said if you have one that's too dark mixing a little bit of this and now it takes a very teeny tiny amount of either of these to change your foundation completely and I will try to insert a clip right here where I'm where I'm showing you that exact thing where I'm changing it and you can see how quickly it changes it on my hand I use these almost every time as a matter of fact the foundations that I have on today I'm testing one on each side both of the foundations needed to have the blue in it because they were too orange for me now if you're somebody that is all of skin tone I have heard people say they're never all of enough for me I have that problem try getting this one and probably the yellow one that they have and you get that green tone that you could put together and fix those foundations so that they could be more of an olive tone for you same goes if you have a problem with not getting foundations that are warm enough for you they do have an orange and they have yellow that you can mix in with these total total game-changing products that I absolutely adore now the last one I want to talk to about is a brush you wouldn't think that a makeup brush could make someone feel like it's a game-changing product but I do this brush that I have from it cosmetics is the most beautiful blush brush I have ever had in my life I feel like this blush brush because of being a mature woman lays blush down absolutely perfectly flawlessly and precisely and I will show you what I'm talking about now I do have another brush this is the new BK Beauty one from Angie hot and flashy it's an angled brush but as you can see from the two brushes they this one is very dense this one is very airy this blush is one from Fenty Beauty and it is a cream blush called cheeks out wait a minute that's not the name petal poppin is what it's called okay beautiful color if I go ahead and I swirl this brush in here from Angie hot and flashy and then I go ahead and I put it on my skin see how it's not really doing its job it's a cream blush yeah and it's pretty but there's not a lot going on there so let's go in a little bit tougher here and we're gonna see about putting more on there okay it's doing a doing its job but it's not depositing a lot I'm gonna take this on my finger and I'm gonna put it down the middle so you can actually see that when you do just apply that with a finger which you can apply apply blushes with your finger I'm not saying you can't when you do apply that with your finger it is very very pigmented so now I'm going to take this it brush that's called the heavenly angled brush so now you can see that it's picked it up but it's right there on the ends so now let's go over to this side as I am bouncing that or pouncing it that it does lay down a little bit more than that one did so I have two coats over here with that blush brush let me put a second coat on with this blush brush and you can see the difference so for cream products this one is a total game changer just perfect sit across the cheeks I want to show you what I'm talking about I have quite a bit already on there but you can just use this in such a way that as you're bouncing it really does lay it down just absolutely beautifully flawlessly there is not too much color on there at once if you want to you can build it up even more which I probably shouldn't do because then I'm gonna have like major major blush line but I want to show you this brush for me as soon as I got it I was like this is heaven had no pun intended as far as the heavenly lux brush goes but it really is a heavenly brush for blush that I absolutely love and I have two backups of it because I feel that strongly about it I love a duo fiber brush for one that it picks up cream cream products and picks them up and it deposits them and they're absolutely gorgeous and if you are somebody like I have in my lap right now the super shock blushes if you're somebody that has had a hard time with those blushes and you haven't been able to get them to go onto your skin being you know a different formula that will get them onto your skin so pretty any duo fiber brush will work but this one is airy enough that I love it and how well it deposits any cream product okay so that is my seven products for today thank you so much for spending a little bit of time with me welcome if you're from Kimberley's channel I do hope that you do decide to subscribe thanks for stopping by all of my regulars you know how much I love you dearly and all of your support are there any products that are game changers for you that you would like to tell us all about let's get a conversation going down there in the comment section I always love to hear that hope everybody is doing really well I'll see you in my next video and I love you to bits goodbye my friends