 Hello everyone and welcome back yet again to another crust of rust this time. I wanted to tackle Functions and closures and the function traits and function pointers because Sort of like the situation is with strings in rust There are a lot of different Function things or function like things and the distinction is not always clear and I feel like It would be useful to just sort of get a survey of the scene What we're going to talk about today is like Not necessarily a Super large area. We might be able to cover it in as little as an hour. I don't know But but I do think it's useful to have some information about the distinction between these different types and traits and how they interact Just because you're gonna come across them a lot like especially things like being generic over functions is I Think something you run into a lot in the rust world where you want to sort of have these nice Like callback functions or you want to be able to write things in a slightly functional style And then function pointers just come up everywhere And so I wanted to to make sure that everyone sort of understands that fairly Versatile primitive in rust Before I start I do want to say that I wrote a book it is called rust for stations It is the it's intended to be basically a follow-up to the original rust book the rust programming language by Stephen Carroll, so this book sort of picks up where that book left off and tries to give An introduction to all of the slightly more intermediate slightly more niche slightly more I Don't quite want to say advanced although maybe that is an appropriate word to it tries to cover more of what you've run into After you know rust the language and you want to know how to use it better for particular use cases And it focuses on things like idiomatic usage But also like how some of the more complex building blocks of the language work and how you can use them effectively The e-book version is now up on no starch press And the print book is like with the printers And I think the goal is like early November for it to actually get printed and shipped from those starch It will be get like distributed by I think Penguin Random House And because it's like global shipping problems it might take a little bit longer to get the book If you buy it through other retailers, but it will be available on like Amazon and stuff already as well and Yeah, it's a lot of what I talked about here is covered in that book to please get the book It's a good way to support me as well if if that's your cup of tea All right now let's get into functions and closures and traits so when we talk about Functions in rust generally what people think about and in fact, let's let's start a new Lib over here or something. Let's start a bin and we're gonna call it We're gonna call it Closure See it doesn't work. I was thinking of closure in the sense of like Feeling yellow like you get closure in something, but it has the same as the types. It doesn't really work. Let's go with call me And what we're gonna do in this is Well, here we have a perfect example, right? So fn main is a function Perhaps unsurprisingly and fn main specifically has a type like if I in let's say I Go bar over here So if I in the main set let equal let X equals bar Okay X now has a type. What is that type? Some of you might say well, it's a function pointer It is in fact not in rust in fact if we try to look at the type of this It sort of looks like it's a function pointer, but it's not actually what this is is a function item Which is subtly different Notice here. I'm not calling bar. I'm just taking the sort of identifier of bar and The type of X here is a function item, which is a zero sized value That is only carried around at compile time that references the unique function bar And in fact if if bar was generic then I wouldn't be allowed to do this Because this does not uniquely identify a function. I would have to do something like I 32 for example So this now specifically names bar differentiated with the type I 32 as T But if I now said X equals bar you 32 Then you'll notice here. I get a mismatched type error and That is because this is not a function pointer, right? The the signature of this function right is that it takes no arguments and return nothing So bar I 32 and bar you 32 are really the same in terms of if you thought of them as a function pointer But that's not what X is here X here is a function item That's uniquely identifies the function bar that takes I 32 as a T if you try to assign that bar That takes you 32 as a T. They're not the same type And if we tried to if I if I skip this and I tried to do something like print line Mam size of X size of I Forget what it's called This is size of Val size of Valia And CD call me and cargo run You'll see that the size of this is zero zero bytes So it doesn't hold a pointer at all because this is just a an identifier that the compiler uses to identify this unique instance of this function now Function items have a coercion defined for them into function pointers though is I can define a function bass That takes a function pointer, which is written like this. Let's I'm gonna make this a little Just give it a better signature and then change bar to match So bass here takes a function pointer. That's what this signifies so here I'm saying I take a function pointer. So an actual like Pointer sized value to any function that has the signature 1u32 in and 1u32 out and I can call bass with bar u32 I Can also call it with bar I 32 in both cases what happens here is that the compiler Coverses the function item type into a function pointer type So that this function can be called So inside of here if I now print size of Val of f and Run this What oh Zero that's fine You'll see that here the the size of the values eight because it's an actual function pointer and that's necessary because These two point functions are distinct from one another right there are two instantiations of bar for different types, which means they have different code, right? They've been optimized separately. They're just they are different Chunks of code that happen to share sort of a generic name But we need to pass the the pointer to the first instruction of one instance of that function When we call bas the first time and a different function pointer the second time around But this does allow us to be generic if you will over which exact function is passed in as long as it has the same signature right and And of course if you passed in something that was the wrong signature like if I did this Right, then now it wouldn't compile because it'll tell me the it expected a function pointer To a function that had the signature you 32 in you 32 out what it found was a function item that has a Different signature that is it's a void function. It takes no arguments and returns no value And crucially here you see it doesn't course here because it doesn't match the required type But what we learn from this is that? Function items and function pointers are different from one another but function items are Coercible into a function pointer and part of the reason we need this coercion is because if I do this If I didn't ever call this right then now Even though I've named the function bar with t instantiated to I 32 the compiler doesn't actually need to Generate the code for that function there. It's no under no obligation to because it's never called however here I'm calling Baz right and so and I'm passing in this course to Computer needs to be able to produce a value that's a pointer to that body and therefore it has to generate the function So that it can get generate that function pointer in the first place Of course if the whole thing get that code eliminated then that won't happen but but in general this forces the compiler to actually have to Generate this function body in general of course you'll do the same here like you're unlikely to just assign it and then leave it But but regardless All right video choppy this time What is going on? Oh It's fine now. Okay, it's fine now. I'll keep going if it's fine now. If it's not fine now, I'll stop What do you think fine not fine? It's really weird that this is happening. I did upgrade OBS. So maybe it's OBS That's very very strange find on twitch, okay so the the takeaway from this right is that a Function item uniquely identifies a particular instance of a function Whereas a function pointer is a pointer to a function with a given signature and you can turn one into the other But not go the other way around and This of course would would then work again All right, so That's function items of function pointers and then we have this thing called closures. So you might have seen Is isn't quacks is like the next Metageneric type I think So this takes an F Where F and let's start out with is Just fn okay, so quacks with bar U32 What does this do? so this also works and What we're saying here is That quacks is a function that is generic over some f where f implements the fn trait And notice that fn with capital F is different from the lowercase fn that we use for a function pointer although the Construction is the same. So if I wanted to say it takes a u32 and returns to u32 I do it in the same way, but one is a trait bound and the other is a function pointer type So this is not a type. This is a trait and Here there are three different traits. So if we go back and look at The list of operations. There's fn fn mute and fn once and the distinction between these is the definition of the trait Specifically fn takes a shared reference to self Fn mute as his name applies takes an exclusive reference to self and Fn once takes an owned reference to self and the implication here as the name implies for fn once You can only call an fn once a single time and at that point You've moved the value of the the function that you wanted to call and you can no longer call it again Fn mute you how you can only call once at a time So you need a mutable reference to it in the first place And if you have one you can call it multiple times, but you can only call it once at a time And this is important for example, if you stick an fn mute in an RC, you wouldn't be able to call it Similarly, if you've given a shared reference to something that's fn mute You also cannot call it. This is also why in general an fn mute If you passed it to multiple threads like through something like rayon Then you couldn't call an fn mute for multiple threads at the same time because it wouldn't be exclusive Fn on the other hand is sort of the equivalent of having a shared reference in that you can call it multiple times And you can call it multiple times at the same time or at least through a shared reference and The reason for this will become clear once we look at what closures are and what they do But let's first think specifically about what this means for function items and function pointers So a function pointer And in fact a function item has no state, right? They are standalone chunks of code and they don't reference anything in any other stack frames, right? They don't reference any memory that's stored outside of themselves There's no lifetime associated with a function pointer or a function item is the other way to think about this And what that means is that they don't really care about self, right? Like for them all self contains is well Nothing really for a function item and for a function pointer self is the pointer to the function But there's no state to really mutate there. There's nothing to move and therefore function pointers and well function items cores to function pointers and function pointers implement all three of these traits So if you have a function pointer, you can pass it to something that takes an fn once You can pass it to something taken fn mute and you can pass it to something that takes an fn The one way to think about these fn traits is that they are sort of a a hierarchy where Anything you can you can almost think of it as Here implement fn once Implement fn for f where f implements fn mute We wouldn't actually ever write this but you you can think about it this way and the reason you can think about it this way And I'll show you in a second It takes no arguments. I am lying. It should be this So If we go back here to Yeah, so as you see in the documentation do since both fn and fn mute are sub traits fn once Any instance of fn or fn mute can be used where fn once is expected So in other words fn implements fn once So start from there and then we can and the reason for this is if it requires if we're given an owned version of self that is So far we've only talked about function pointers, but if we're given an old owned reference or an owned self rather Then we can trivially produce a reference to self. So I can really then do the sort of fn call of Like I can easily translate one to the other Right because if I have something that implements fn and I'm given a self I can just take a reference to it and pass that to the fn and Similarly, I can do the same thing for something like fn mute If I have an fn mute, I'm given a self right, so I'm given ownership of it I can trivially just generate an exclusive reference to it and Similarly Fn mute I can easily implement for fn right, so I'm given a Mutable reference to self well I can just turn that into a shared reference and then call the fn version right because These are sort of strictly more powerful Does that does that make sense like why these end up forming a hierarchy? So anything that implements fn also implements fn mute and fn ones anything that implements fn mute implements fn Once as well, but not fn because you can't if you're given a shared reference you cannot produce an exclusive reference And fn anything that implements fn ones only implements fn ones all right Does that make sense so far? Yeah, I don't know what's going on. I think it's my internet that's being weird So we have this hierarchy and and I think it'll make a little bit more sense why why this Hierarchy is is needed once we start looking at closures because again a function pointer implements all of these because it doesn't actually actually care about the ownership of the function pointer because it's just a pointer to code or you can think of it as a Function pointer implements fn and therefore it also implements fn mute and fn ones but by virtue of the same rules We just talked about And So that's why here when we require an f that implements fn We can easily pass in a bar you 32 because it coerces to a function pointer which implements fn And similarly if I made this fn mute This would still work and if I made an fn once this would still work because function pointers implement all of them In terms of actually calling them though, you'll see that here. Let's say that I wanted to actually call The f that I was given if I have an fn once then I Need to take it by ownership if I did this for example Reference to something that implements fn once then the compiler won't let me call it because fn ones requires ownership of the f It requires that I be able to move the f into the call Which it's telling me it can't do because it's behind an exclusive reference right and now similarly if I Get an fn if I say that it's an fn mute then now all I require is an exclusive reference which I have and therefore I can call it But if I got a shared reference I couldn't call an fn mute because it can't be borrowed as mutable Which is required in order to call an fn mute, but I could if it was fn Right, and so then we've ended up going full circle here All right, so let's now go back to why are these different like we already mentioned how function pointers implement all of these By virtue of implementing fn. So why do we need this distinction? The distinction comes up once you start start talking about closures. So fundamentally a closure is written this way So You've almost certainly used closures if you haven't this may not be the stream for you But basically it takes arguments which are passed in between these And it has some body that returns the contents of those let's say here We can annotate these with the takes an i32 and i32 Right, so this is a closure. This happens to be a non-capturing closure. So in general Closures are named closures because they close over their environment That is they're able to capture things from their environment and generate a unique function If you will that specifically absorbs or uses or references data from the surrounding environment at the time when they're created In this case it doesn't capture anything from the environment It only references its own arguments and therefore this is a non-capturing closure And what's interesting about non-capturing closures is that they are Coercible to function pointers. So I can actually call Baz with f if it had the right signature Which we can make it do So see here Baz takes a function pointer and Closures that do not capture over their environment can be coerced to function pointers They can also be they also implement that the standard sort of function traits, right So they implement fn for example and because they implement fn they also implement fnmute and fn1s and all the way up But if I have a Closure that does capture from the environment. So let's say that I declare a Z which is a Non-copy value, so it's going to be a string new if it's copy I'm going to hand wave a little bit and say that we don't want it to be copied because it makes us Reasoning a more complicated than it needs to be so here I'm going to declare this closure as just consuming Z and You'll notice that now it it no longer can be passed to Baz and if we look at the compiler errors You see that it says closures can only be coerced to fn types if they do not capture any variables And in this case it does capture a variable, right? It captures the Z variable from from its own stack now Let's say that all the closure did was like print line Z Right, so it doesn't actually move Z It just takes a reference to Z But nonetheless it captures over the environment and that means that you cannot represent this closure as just a function pointer Because in order to like this this closure think of it as the compiler generates a sort of anonymous struct for the closure that contains a Fields for everything it captures from its environment because when this closure runs it needs access to that to that information So you can sort of think of this as it generates like a f closure struct That has a Z field which is a string and Crucially right this is an a or if you will scope Which is a reference to the surrounding scope And then you can think of it as it implements fn for f closure and it can do that because if it's given a Shared reference to self then it can still call that closure in the sense of this Implementation is going to be sort of copy paste from closure definition Right, it'll just turn that into self dot Z And that works just fine because this is just a shared reference anyway So you can you can't actually write this code but you can think of this is what the compiler generates and Therefore you see why it needs access to the Z the function pointer Which would just be a pointer to like the start of this code block would not be sufficient It needs the additional state of this Z Does that make sense? so far Okay, so so now let's look at what happens if I make this mutable and I say that this is gonna z dot clear so clear is a Method on string that takes an exclusive reference to self and it clears the string it doesn't deallocate anything but it clears the string So now the f closure that gets generated the sort of state for the closure has an Exclusive reference to a string because that's what it needs to capture in order to call clear Right, it can't capture a shared reference because this requires an exclusive reference and at this point If we now again imagine that it sort of copy paste the the body This clear won't work Right self dot Z dot clear and the reason this isn't going to work is because the Z here Requires an exclusive reference, but all we have is a shared reference to self to the the sort of closure state if you will and so we can't actually get an exclusive reference through through that even though we have one here this ends up being a Shared reference to an exclusive reference to a string which is only Usable as a shared reference to a string And so this won't compile or in other words When you have a closure like this it cannot implement fn it can implement fn mute Right because here we now an exclusive reference to the closure state Which has an exclusive reference to the Z and therefore we end up having an exclusive reference to the string and this works just fine and Again anything that implements fn mute implements fn once and so therefore this would also implement fn once And so so this closure is an example of something that implements Let me comment this out just to avoid the compiler errors for a sec So you'll see that this doesn't course to a function pointer, right anything that captures cannot be It we we try to call quacks with it But it says the closure is fn mute because it mutates the variable Z here Right it expected a closure that implements the fn trait, but this closure only implements fn mute and That is indeed exactly what's going on as we just discussed it cannot implement fn because it needs to mutably borrow for a mitts environment Right if I made this take an fn mute Then I would need to take at least this or I could just change this to be owned right then now This And I don't pass in a shared reference to it so now Now f is just fine to pass to quacks because all it requires is a An fn mute and this closure is fn mute So what then you might ask is okay, what about this fn once well Let's imagine that what we wanted to do in here was actually drop Z So to drop Z we need to have ownership of Z which means that we need to move into the closure Right, so Z gets moved into the closure And that means that you just like obviously cannot call this closure more than once because to call the closure you move Z But if you tried to call the closure again, you would have to move Z again But you can't move Z Z again because it's already been moved or If we expressed it this way like we've been talking about so far This no longer has sort of a lifetime here. It owns the Z string, right? Or you can think of it as it still has scope But there's just no use for that scope if we try to implement fn mute and this tried to call drop self It's not possible for us to drop self here because calling drop requires ownership of self But all we have it as an exclusive reference therefore this won't work We can however implement fn once because fn once is given ownership of self and therefore we can drop self dot Z Sorry, this should have been self dot Z all along So here we are allowed to because we get ownership of the closure state And so this implements fn once but it cannot implement fn mute and it cannot implement fn And you can see this also by if I comment this out again because you can't actually manually implement the fn traits at the moment You'll see here that it says That we can't this call to quacks is not valid because it expected a closure They implement the fn mute trait But this only implements fn once and it implements fn once because it moves the variable Z out of its environment here So that's sort of the the path that we go around here, and you might have seen move closures So you can write the move keyword before the definition definition of a closure closure and what that means is So closure capturing is actually a little complicated Because here the compiler sort of knows that what the closure needs is an owned version of Z It needs to own Z and therefore it it determines that it should move Z into the closure When we called Z dot clear the compiler sort of automatically figures out that all we need is an exclusive reference to Z And therefore it only needs to Move a an exclusive reference into the closure and not all of Z itself And similarly when we had print line of Z it realized that all you need here is a shared reference and therefore it only Moves a shared reference into the closure rather than all of Z And that logic generally does what you want But but it's not perfect and there are some cases where you want to move a value into the closure even though you don't technically need it an example of this could be if The closure you want the closure to be the thing that drops the value that is when the closure exits You want the value to be dropped So currently the way this is this is organized is that Z the string will not be dropped until the end of this scope down here Right at the end of main Because it's not dropped in the closure if I write move here what I'm telling the compiler is move Z Into the closure and so now Z is dropped here We still only needed a shared reference, but we're telling the compiler I want you to move into it here and now because we're moving Z into it we end up in the same state of We actually need to own the Z and so we can only implement fn once we cannot implement fn mute or fn Does that make sense actually? I don't know if Why does this compile? Maybe I lied to you. I think I lied to you. I think the move it'll still apply this heuristic Yeah, you're right the the reasoning for this is actually slightly different which is Well that it is true that you can make a drop-in here But but it's also because if I do this the lifetime of the closure is tied to the To the stack here so so maybe if I if I give a slightly different example So make fn is going to return an impulse Fn once sure why not and it does Z equals string new and then it's gonna return a closure that is gonna Print line Z Right, so here is an example of I want to return a function, right? I'm not going to tell the caller whether it's a closure or not but I'm gonna return something that's callable and it's only callable once and Or in fact, let's say fn I want to return something that's callable and I want it to be callable multiple times which this one is right It's just printing Z over and over the problem is that right now this borrows Z Right and so the closure that's returned sort of has a lifetime that's associated with the The sort of self of this function, right this you can sort of read as Z is like a reference to Z that's moved into here and This reference has some lifetime Right, which means that this closure type really has that same lifetime but if you don't specify a lifetime it assumes that it's static and So here we're promising to give back something static but the closure that we return isn't static because it references this Z and so there You can apply the move to say move Z into the closure and now this closure is fn Because every call to the closure does just References the string that's stored in the closure self contents and that's fine, right? You can have multiple calls that all get a shared reference to the closure state Which gives a shared reference to the string for printing If it would not work if this tried to say Drop Z because it wouldn't implement fn Right because once you've called it once you've now moved out of self And so you cannot call it again And so this is where move is useful now one downside of move is that move means move everything There's no like move like let's say we had X as well This will move both X and Z into the closure And that that is oftentimes what you want right in here. We wanted to return something that was static But that's not always what you want But the way that you work around this is you either don't use move right if you can get away with it Or you use move and if you if there's something you specifically want only to be borrowed you do like Let X to equals reference X and use X to in here Now this this won't work in this case because this makes it no longer static But that's the way that you can move some things by reference and some things by ownership And then there are many ways to express this like sometimes use shadowing if you don't care about the old value You could introduce a new scope So that you don't have to deal with this later Regardless of how you end up doing it. This is the way that you would move some things but borrow other things into the closure Does that code create a new string in static every time make fn is called Yes, so the way we had this if I go back here And remove Z and make this pub. I guess This every time you call make fn. It's gonna allocate a new string And then return a closure that owns that string that was allocated You can't choose per variable which to move or not move move is all or nothing so that's why you end up with a pattern like this for example to Specifically say the thing that I want to move in here the X is actually a reference to the real X So I want to move the reference And of course if you do move something into a Into a closure then now the closure as as we saw up here, right the closures Collectively or the closure owns the string and so It's only when then any call to that closure uses that same string And it's when that closure is eventually dropped like the actual Variable that holds the closure or the allocation that holds the closure when that gets dropped the state structure for For that closure gets dropped and therefore the string also gets dropped All right, so we've now actually talked most of what's Important to grasp about these function types But there are a couple of more things I want to touch on the first of these is around din Fn So din fn So just like with other traits you can also use the fn traits through din to get dynamic dispatch And you specify the full function signature and you just put din in front of it and this works just fine In fact here, I can just call f And similarly if this was a bit din fn mute, I can call it As long as I take this as mute Right, so that works fine. And if it's an fn ones, I can also still call it. So so far so good This was not always the case interestingly it used to be that there was this special fn box trait and the reason for this was that This is a little bit of an anecdote, but it but it is an interesting one I think it used to be that box din fn once And in fact box din fn anything did not implement fn anything So basically it used to be that box din fn did not implement fn and similarly for fn mute and fn ones and The reason for this actually is kind of interesting So let's imagine that we were the people trying to implement this Because I do think this gives a sort of interesting insight Let's imagine that we are the standard library and we want to implement. Let's say fn For a box din fn So we have to implement call. We take no arguments. We return nothing and now the question is what do we put in here? right so Self is a box But we have this challenge here of Like let's say I do self dot zero let's say that's how I get at what's inside the box and I want to do call With no arguments, right? So it seems like this is all I should really need to write sort of dereference the box And then call the thing that's inside The problem is what is the type of self dot zero here? If we were to write this out as a variable, right and Let's let's take fn ones just because it's more clear what's going on So I move out of self dot zero to get at the thing that's inside the box And then I do x dot call What's the type of x the type of x here is din fn once But this type is not sized So how much space does x take up on the stack here? Remember din in general is unsized, right? It's it's not sized And that's why in general for box you always need either a reference Or an exclusive reference or something like a box around it You can't have a freestanding din because it's not sized so the compiler wouldn't know how to range the stack frame for this method call And so it's kind of interesting if you look back sort of historically And this is going to be bright for a second. I'm sorry about that So here in the release announcements for rust 135 You see it was like a big thing that now these traits are implemented for the appropriate box types And previously those is fn box that got to do special compiler magic And as you see here this sentence This was ultimately due to a limitation on the compiler's ability to reason about such implementations Which has since been fixed with the introduction of unsized locals And so someone said why couldn't you take a reference to the box contents? Well, this is why I chose fn once because here to call it. You need an owned self So you can't take a reference to this because if you had a this you couldn't call it because fn once requires ownership of self and So slightly similar issues arise with fn fn mute And then so there is actually an rfc specifically for unsized r values Which is required for this implementation to exist and this rfc the rfc is landed But there's lots of implementation questions and basically the compiler gets to take advantage of this particular feature But it's unstable. So you like you can opt into it on nightly But you can't generally use this on your own code on stable But it is required for this particular thing to work which I thought is like an interesting tidbit And there's all sorts of cool implications of what we could do if we got unsized r values, but but that's neither here nor there So when you have boxed in and then some fn trait that just works now You don't really have to treat it specially like in general din just works But you do have to keep in mind that if you get something like a How am I gonna demonstrate this best So this implements fn, so let's say I here gave a din f din equals So this works just fine, right? I turn f into a dynamically dispatched fn This is sort of like a function pointer except it's allowed to Right, so it has a it has a self Basically it constructs a self for this closure and then uses that as the data pointer of the V of the Dynamic dispatch and the V table just has the call method So that's so fine as far as fine The challenge here, right is let's say that I tried to make this fn mute So now I have a Din fn mute, but all I have is a shared reference to it and therefore I can't actually call it because this Doesn't implement fn mute. This only implements fn because all you have is a shared reference. So when you use Dynamically dispatched function traits You need to make sure that the sort of the wrapper the indirection type the the wide pointer type that you use Allows the kind of access that you actually need in order to call that function, right? So in order to call an fn mute, you need to stick it behind a shared reference In order to call an fn. All you need is a shared reference In order to call an fn once you actually needs a wide pointer type that allows you to take ownership right So, I mean I would have to box new And of course again the same hierarchy applies So if you have a wide pointer type that allows you to take ownership then it will also work with any of the sort of weaker function or Less restrictive function Traits so I can have a box din fn mute because if I can take ownership that I can certainly get an exclusive reference If I can take ownership then I can definitely get a shared reference So these are both fine and will implement the appropriate trait outside of the wide pointer And if you think of something like arc right so standard sync arc standard sync arc will allow you to To stick Unsized things into it, but an arc but by necessity will only give you shared access to the thing that's inside and therefore arc din of fn implements fn, but arc din fn mute still only implements fn Or in fact, then can't implement the trait rather. So here if I say this takes fn Whoo Maybe they haven't implemented this for arc. Maybe they've only implemented for box Interesting so I'm guessing then that this implementation doesn't actually exist for for arc yet if we go back to look at Fn for example you see there's an implementation of fn for box Of f where f f implements fn, but there is no implementation of arc. I wonder why that is That implementation should exist interesting Right and we can use the intuition we've built up so far for why that should be the case an arc supports unsized values, right basically arc can support being a wide pointer and therefore it can hold the din fn and If it can hold the din fn and it's able to give you a shared reference to the closure state Then it should be implemented implement fn because all that requires is being able to get a shared reference to the closure state So this suggests that there's sort of an implementation missing here and it might be because Because of this issue we were looking at about unsized our values. Maybe it's like specialized to box somehow But but that would be an interesting implementation to add That's pretty good. Okay, so about an hour. I think I guessed about right the the last thing I wanted to touch on was Const fn's so What's a good example of this? Let's try to erase some of this stuff Okay, so let's say I define a closure that takes a That just returns zero right, so this closure is a Constant closure you could evaluate this closure at compile time Right, this is sort of equivalent to const fn like make zero that returns a u-size or I guess I 32 is the default, right? These are both the same in the sense that they're both callable at compile time They're basically both const But the question becomes let's say that I want a const fn Foo and I wanted to take an f That's let's say fn once and I want my const fn to be able to call f Well, that's not currently okay Because the compiler doesn't know that f is callable as a const right Because we've just said this is an any any type that implements fn once and we don't know that the implementation of fn once is actually const So there's nothing in stable that you can do about this today But there's a sort of interesting pre rfc discussion that's going on and has been going on for a while about is there a way to say I Want to take I want to be generic over any type that implements this trait But only using things that are const Evaluatable and the syntax I've come up with is this and Let's see if I can opt into this easily. I forget what the actual thing is called I might not be named anywhere that I can easily get at What happens if I run cargo R? Let me just pull this up real quick to see what the But what is the name ah Future get a concentrate impulse So we're gonna add this Here and then we're gonna be rust up overrides at nightly and we're gonna cargo R and see what happens Oh Yeah, there's here There's the second one you need to add which is this one So again, as you can see, this is very experimental. Okay, so So what this signifier means this this cons this this tilde cons is that it doesn't actually mean This f and ones or this f must be consed It doesn't mean that I will only take types that that have a constant implementation of f and ones That's what this would mean, right? This would mean f must have a constant implementation of f n ones It's also not question mark. Like you might think of like question mark sized It's not really question mark cons because it's not saying may or may not be consed Which is what question mark sized means rather what the tilde here signifies is Foo will be consed if f is consed if f is not consed then f n is not consed And we haven't talked too much about consed on stream But the basic premise here is if you have a const f n It's callable at compile time or at runtime like you can call it either of them Which sort of means that it's a const f n is also an f n And what this is saying here is that this const f n is only consed So it's only callable to compile time if its generic parameter is also consed in its implementation of f n ones And so that's why this is like new sigil here because it doesn't really mean the same as the other sigils that we have So that that's that's why I mean this this is not a full rfc yet It hasn't been merged so this is very very experimental as you can see with like the two features We need to opt into But but it is an interesting thing to keep in mind and something you will see going forward as we start seeing more More constification of rust code Foo calling x. Oh, sorry. Yeah, so if foo calls x here Foo, what am I missing? Expected zero arguments. I think this is just rust analyzer being confused because it doesn't know about these features Right, so this runs just fine Why oh because string new is const If I said string from Foo this is not const. Oh a string from const to What is not const? Vec 1 2 3 What is not const? Oh? Sorry, it's fine. Okay, so right. So this is what I was. Yeah, this is what I was getting at Let's let's do a const of n Test foo that's how what I need to do The the problem here right or the reason why I have to do this is because main isn't const So there's no requirement that foo is const So I think this can go back to a string from foo now a string new is const So in main I'm allowed to call foo because I don't require foo to be const I can call it even if it's not const and the closure here So the implementation of fn1s is not const and therefore foo Sort of genericized over this closure is not const, but that's fine for main So if I comment out this for a second You'll see that this runs just fine But in a const fn In a const fn you can only call things that are themselves const So here if I try to run it, you'll see that it complains Expected an fn1s closure found this closure And here you'll see the errors aren't very good and that's because it doesn't the error reporting doesn't know about this const fly yet, but the actual complaint here is that const fn's require that everything they call is also const So this requires that foo when generic over this closure is const But this closure is implementation of fn1s is not const Therefore foo is not const and therefore this call is illegal If on the other hand I use string new here, which is const is maybe not const Interesting That's interesting Does it work if I do this? weird Yeah, I think this is a evidence of this being a very experimental feature But the intention is that if the closure is itself constant or constant Evaluatable which string new is because I believe string new. Yeah string new is a const fn then foo should also be const fn and therefore should be callable from const context Const of const events are stabilized, but This kind of bound is not stabilized and so error reporting doesn't know about this kind of bound And so it doesn't really talk about it in the error message you get it does know about const fn more generally All right, I think that's all I wanted to cover about Functions and closures and the the traits and types that are involved are there questions about any of the stuff that we've talked about so far Like any of the any of the bits that you want me to go over again And I'm sad about the audio issues, but hopefully at least in the video in demand. I'll chop it up a little No, people seem to be happy. You've generally followed Any questions about anything else now that we we ended up with sort of a relatively short stream, which was kind of as expected Anything that I can answer outside of this particular topic Sometimes you end up with complicated lifetime bounds with for our So let's get rid of the const fn stuff for a second Yeah, okay, so an example of this is If I write a F and it takes an f and I want to say that f is a function that Let's imagine that it's a map function. So it's gonna be given a sort of I Don't know. It's gonna be a given a reference to a Stir and it has to return a stir So let's imagine that I I want to write a function like this which maps a string And I want to try to call quacks with a closure that takes x and returns x So far so good, this is not a problem. This this works just as expected But There are cases where this gets a little bit more complicated in particular like remember in bounds you usually Although not always but you usually have to specify Lifetimes what are the lifetimes here? Right whenever something returns like that you can often omit the lifetimes But if we were to try to specify what the lifetimes are here, what are they? Because there's no lifetime here if we tried to fill in the lifetime for this for this trade bound What actually is it? There's no tick a because we can't assign a tick a here what we're really saying is we want f to Sort of reuse the lifetime that it gets in in its output We want to say that it's allowed to reference the same thing that was referenced in its input And this is where you get this special for syntax And what this syntax means this is the actual desugaring of that syntax This you can read as for any lifetime tick a F is implementation of a function from a stir with a stir reference with a lifetime of a to another stir reference with the same lifetime of a So it's not actually that's comp that complicated. It's a way to say that This this needs to hold for any lifetime. This is what the bound should be It's very very rare that you actually need to give a for bound like this It does sometimes happen if you if you have trait bounds that have lifetimes That are not f n's the compiler is pretty good about inferring it for anything that is of Fn type or fn muter effort once it can usually figure this out But once you start having other traits that aren't fn in here you sometimes need to reach for four But but it should be very very rare You Be doing another Q&A session sometime I will but I don't quite know yet I really want to get back to the hazard pointers library So I'm trying to find time for that too How much will I enjoy rust for rustations over 9,000? How do you broadcast both video into your screen at the same time what software using I'm using OBS very happy with OBS If I want to pass a closure to an ASIC function the closure needs to be static, right? How does this kind of closure capture its environment? If you want to pass a closure to an ASIC fn, you can just do so There's nothing Preventing you from taking any fn. It doesn't need to be static. It's more that usually with Usually with futures especially if you want to do something like Tokyo spawn of the future you get back then Tokyo spawn just like thread spawn requires that the argument is like static and If f here is not static then the return future will also not be static, right? If we sort of think of the desugaring of this right, it's fn this to impulse future That's really the desugaring of of this and Imple trait just like async fn automatically captures the lifetime of its inputs So if this input is tied to some lifetime then the output type will also be tied to that same lifetime Which means it will not be static unless the input is static and so that's why you often end up having to add static to Generic types that you pass into async functions. It's not because they're required like if I If I just directly awaited here, then there's no need for the function to be static the future that's returned to be static It only comes up if you try to do something like spawning where the future needs to live longer than the current stack frame You often need to pin it I mean you should very rarely need to pin things manually In general, I wait since I should take care of you All right in that case, I think we're gonna end things there Thanks everyone for watching. I hope you learned something go teach someone else what you learned and I will see you all In a few weeks, I really want to do more Hazard pointers, but I just need to actually find the time to do six hours of coding All right. Bye everyone. Hope you enjoyed it