 everyone welcome back it's been a long time since last time but finally I'm now back I also have a new microphone which is really cool one of you gifted it to me and that is really exciting that I can do something where people feel excited enough that they will contribute to help make things better I am John as you can see from screen I've been doing these streams now for over a year and I do streams on all sorts of rust related things some of them are live coding streams some of them are open source contribution streams and some of them like this one are more sort of educational streams and so there's a Twitter where I announce upcoming videos I also have a YouTube channel where a bunch where I post all of the all of the sessions we have after the fact so you can go back and look at any of the old videos if you want to I also for various annoying reasons I can't actually have a patreon in the US and so instead I recently started this Amazon wishlist that's linked to from my Twitter so this is how I ended up with this microphone if you have something to spare and you want to show your appreciation for what I do then please find something here that you think could be fun to fun to buy to help with help me and help with the with the streams so today's stream is going to continue on from a stream that we did a while ago which is this one the what how the what and how of futures and async await in Rust so this stream we talked through like what are futures how do they work what is Tokyo what are run times how does all this machinery fit together what is async await how does it interact with futures how does it change the way you write code and as part of that video we also touch on this notion of pinning and we talked a little bit about what that means but I feel like there's a lot more space for being able to talk more about pinning and that's what this video is going to be about it's going to be about what is pin what is unpin why do we need them and how do you use them I'll try to keep an eye on chat for questions as we go for these educational streams it helps a lot to sort of observe what you understand what you don't as we go so if there's anything you feel like I'm not entirely sure what he means here please ask a question because that way there are probably other people who wonder the same thing as you do and I can then address it and also your the question and the answer will then be recorded in the stream for other people to learn from right so the reason we're doing this I have this live live stream voting site where a lot of you have voted in the past and in fact I can just pull it up here so this you can vote for what upcoming stream ideas ideas are most exciting to you the current winner is this open source contribution stream which I think is really fun we've done one in the past but I have this idea of I really think it's important that someone do a stream on pinning and so I decided well I should do one and so I did this poll on Twitter to to ask people what they wanted to see and as you can see the overwhelming majority of the people who voted wanted to see like a deep dive into pin and unpin and so that's what we're doing I will try to also take a few of the practical considerations into into account when doing this so my hope is to also get to look at some code and how code changes when you're moving to pin but it will be primarily about sort of the the ideas of pin like why do we have them and how do we use them great all right no question before we start that is great so let's just dive right on in so as part of recent Rust release pinning is something that landed in the standard library and pinning is usually talked about in the context of async await because that is primarily where it comes into use in Rust today but it's not really about async await pinning is a more general concept that happens to solve some of the issues that async await ran into so what we're going to do today is sort of look at why do we need pinning in the first place why do we need the concept of pinning and what does it even mean for something to be pinned and part of that is going to be discussing the pin type so pin is a is a struct it's not a trait or anything it's just a struct and also part of it is going to be talking about the auto trait unpin so auto traits are things like send and sync and unpin which are automatically implemented by almost all types in the Rust type system and that the compiler will auto implement for your types if all the things it contains are already unpin we'll also look a little bit about more sort of tools for dealing with pin but we'll look at those later so what is like the fundamental problem the pinning is trying to solve well pinning there's a lot of text here that you can read if you want to but we're going to look at this from sort of a code perspective which is as follows let's say that you have some struct foo now let's call it you have some struct parsed and parsed in parsed you want to have a bunch of bytes and you want to have like a wrapper which is actually let's do this owned parsed and you have a struct parsed let me just type this out and then it has a bunch of fields that reference into a so for example like name so the idea here is that you write a parser and your parser is going to generate these structs called parsed and they're going to have references into the byte stream that was parsed in the first place right so this is totally fine it means you can write like I guess if we wanted to write it this way it takes like a bunch of or it takes an input string and a return something like a result parsed right but notice that there's a lifetime here who knows what the error type is there's a lifetime here in parsed because it right it refers to data that is in the input and now I imagine that you wanted to write a version of this where they're called owned parsed that didn't have a lifetime and the idea here would be that you have a buffer that has a bunch of bytes and you want to also have the parsed representation stored with that buffer so now you can sort of think of this as this parsed its lifetime is really sort of like self right it's this parsed lives for as long as this buffer lives sort of right it's a pointer into it has a bunch of pointers into this buffer I guess it should be UA just for consistency so this has a bunch of pointers into this buffer so as long as those bytes don't move this struct is fine and you can move it around you can keep using parsed and you don't have to worry about the fact that there's sort of a implicitly a lifetime tied to it as you might wonder well this seems like a very niche use case and it's true but it happens to be that this pattern is pretty common in fact for async await if you write something like an async fn foo and let's say it takes no arguments and it doesn't really return anything and you write something like what's it doesn't really matter but let's say we have a buffer of something here and then we do something like I don't want to write out the things here but let's say we do something like read mute buff thought await and then we do something like write buff await fine I guess this will take a a mute in async read plus async write or something it's not exactly important what this does but the idea is that you have an async function you use some awaits in it now this might not immediately rewind you of this there's there doesn't seem to be anything similar but if we dig in a little bit more we can actually see that these are the same and give me a second so here when you call foo the first time let's desugar this a little bit first right so this is really this right these are the same mostly I guess technically there's something like an async move here sort of like this right the question is what does a wait to desugar to well really what happens under the hood here is that is that the compiler is going to look for every await point and sort of turn it into a yield I'm gonna I'm gonna tell you what yield means in a second so let's imagine I guess desugar to something like this the idea here is the first time foo is called it starts executing until it hits the first yield yield gives you a future right this produces a future and now it's gonna it's gonna continue executing that future until it completes but the question is what happens when it completes well when it completes and foo sort of continues from the previous yield point it needs to continue from here it can't start from the beginning because then it would do the read again it has to sort of continue from here and the same thing when this yield happens this produces a future when it eventually completes foo is going to continue from here but what does continue from here mean it means that any of the state that the that we have already it's going to have to be kept in particular this buffer here has to be stored somewhere right because if you're going to continue from here you're going to reference buff right so so this sort of has a pointer up to buff similarly this future also has a pointer up to buff so really what gets generated here is something like a foo future you can think that the compiler almost generates this which is like there's a start and in start you just have this buffer I guess this would be you ate something and then you have a like step one which is a an IO read future right but this really sort of has a lifetime it has a reference it contains a reference to the buffer so it sort of has a reference to start in a weird way and similarly this right future here also has sort of a reference to start you see what I mean because this future is trying to read into this buffer similarly this future is trying to write from this buffer as well both of these types are really the problem and that what we're struggling to express both in this tick self and this tick start is that they're sort of self-referential right they want to have pointers into themselves how would it know the yield it got is the first one so the compiler doesn't actually generate yields but you can think of it as it remembers where it last awaited right so the compiler is going to generate code that almost looks like a state machine right so that foo is going to be called again but when it gets called that it resumes from the right place you could imagine this is some something like an enum right where the next if if you're in start then you execute from here if you're in step one then you execute from here and if you're a step two you execute from here right so it's really just the compiler keeps track of where you are in foo and where it should continue from regardless though all of this has to generate self-referential code code that has pointers into some stuff that it itself owns all right so why am I telling you this well um what was the other uh who's the caller from that code the caller is whoever tries to await this future it's the caller so um why do we need a pin for this well pin the idea behind pin is that the moment you place something in a pin you are promising that you will not move it again and why does that matter well let's stick to the simpler example of our parser here once you have parsed into once you have parsed this buffer you can no longer move the bytes in this buffer if you did then the pointers the parser keeping would be invalid right if you move these bytes then the pointer in here that points to those bytes would be invalid and the whole struct would become invalid so if you have so this is where we get to the pin type so we can look at the definition for pin so pin is pretty straightforward it's just a struct that has a p um where we learn more about p is when we look at the impulse for p so pin p the p has to implement dref so think of this as p is a pointer type um and the target of pin is the type that we want to talk about so for example you can have a pin of a mutable reference right so then the target type of the dref would be a t so for example uh let's see what's the best way to exemplify this um imagine that actually let's bring back our async here async foo bring it back to the way it was all right so uh here so here um the await points right here and the await point right here uh require that buffer is pinned right the moment you have gotten to one of these futures you are no longer allowed to move the buffer because this future and this future both have pointers to it and so this is why um if we look at the future trait uh let's bring that up here so the reason I talk about future here is more because it's the currently the easiest application of pin not because pin is necessarily related to futures so if we look at future what this is so the pole method of future is the thing that drives the future forward is saying that self needs to be pinned and why does it say that well it says that because if you have a future that is like an async function then only when it is pinned is it safe for you to keep executing it or let me try to find the right words to say this um okay let's uh let's think about this just from this perspective first of all so this is sort of going to generate at the point where we're the the thing that we're awaiting here the future that we're awaiting here right let's try to write that out so it's an io read uh and it's a future it has a reference to a buffer right somehow it has a reference to a buffer that is the future that gets generated and like keeps a stream which is a mutin async read right and we're going to implement future for future for io read output is i guess something like an io results u size but the exact type is not important i'm going to leave off some of the less relevant parts here um man i can't type today um okay so this future is written like this but the future of foo needs to include buff right because that there's foo returns a future so what does that future look like in this case um so this does some stuff it's not terribly important but the larger future that is generated for all of foo is something like buff which is a u8 2024 and an io read um which needs to have some lifetime really it has the lifetime of buff now you can't write self-referential structs in rest today you can write them with pointers um but you can't really you can't write them with normal lifetimes right so this future has a reference to buff sort of internally you could imagine this is something like um if you wanted to approximate this you would use like raw pointers or something but really what we want to say is that this future is tied to buff but this would not be okay in and of itself because now if you moved foo future that moves buff right buff is just bytes directly embedded in the foo foo future struct so if I wrote some code like this uh I would say let's say that I had a main I let f is foo so f is now of type foo future we're imagining that this is the code that the compiler generates right it it it magically knows how to make this buff lifetime work out because the compiler can do that so when we call foo we get back a future because it's an async fn and we're gonna imagine that that future looks a little bit like this okay now imagine that I do something like um f dot f dot pole okay and now I'm gonna do let z is f and then I'm gonna do z dot f dot pole do you see a problem here so here if if we think about memory right the memory for the buffer is stored in f it's sort of a particular memory location um and when we call pole this s is going to try to read into this buffer okay so it reads some bytes into that buffer and that's fine because this buffer points into f right and f hasn't moved the problem here is at this point we're moving f buff is no longer the same place in memory but we haven't changed where the buffer in the io read future points it still points into f but we've moved away from f and so when we now call pole on on the io read that we moved into z then now it's going to try to read some stuff into buff but this pointer points to buff which was in f which is no longer there like you can imagine that here effectively f is dropped so if if we uh if we yeah it's just going to be an invalid pointer at this point that is not allowed to read into and so at this point our program would like crash with uh like a invalid pointer or use after free if you're sort of from c world you could imagine it's something like that um so before we continue i'm going to take some questions because there's a lot to unpack here and yeah um let's see how do they schedule the execution uh at this point i'm not imagining that there's any executor i i just imagine that pole is called manually for now that's still sufficient to explain what the what the problem is shouldn't the buffer be a field in a struct so it's a separate memory location yeah so that's why i moved it out to here really what you can imagine here is there's there are some fields that are like the the ongoing state of the async fn future and there's some stuff that's like there's like an enum here which is like progress uh which is um like an enum which is something like read like step one is an i o read buff if we wanted to flesh this out a little then this is really more what it looks like and in fact let's let's write this out this might actually help um so part of the problem with explaining this is that you can't express self-referential types in rust today and so even writing the code is tricky uh there's going to be some return type here um so it's going to match on progress let's ignore the pin for now because we haven't really explained what it what it what purpose it serves here um if we're in step zero then what we're going to do is we're going to do self dot progress is um you can't really say this either you can't have anonymous enums but let's imagine that we do step one um i o read new of uh i guess this also has a new den async read async right right so buff and s here are the the fields of foo anything that's not a future basically any local variables and such right so it's going to create s and then notice here that it takes a mute to buff to the buffer right so step zero is just creating the i o read in the first place and this is where the pointers into the buffer in foo future is taken and if you're in step one then what you're going to do is uh is a mute f uh like i o read future then i o read future dot poll and here the you can imagine it's doing something like it's going to match on this and if it's poll ready um then it's gonna something something something uh step two and if it's poll let's do this in a way where the compiler won't yell at me and if it's poll pending uh then it's gonna return poll pending this roughly makes sense for why this would be the future generated by this async fn right that this is how it keeps track of how far through foo it is right so here if there was another future we waited on down here then that would be the step two part and then there would be a step two here which is if you're now in step two then this is the future to poll um that sounds like we initialize a second buffer to store a temporary value until the async is finished wouldn't that be pretty expensive to do yeah so the the reason we want to be able to write the code this way is because there's only one buffer rather than having to allocate a vector or something we can actually have a buffer that's like embedded into the future that we do the reading and writing for so that we don't have to do any dynamic memory allocation at this point uh i'm not gonna deal with things through off topic right now um okay so does it make rough idea that this is the kind of future we would get or would get generated by the compiler if you wrote something like this just to sort of check the realm the same page there and does it also make sense that without any other considerations this would be a problem right because here in fact we can just do don't need dot f here right the when we first get foo future here uh we're step zero right here we're now at step one and buff points into f yeah or i guess uh i'll read buff points into f here we're still in step one and i'll read buff points into f which seems like a problem because f has been dropped right so this seems like a problem so the question becomes how do we fix this uh in fact let's just for for now this is the old future trait this is how futures used to look right that you just took mute self and then you can run into exactly this problem assuming you had a way to write self-referential strikes right this would not be okay also how do we fix it so this is where pin comes into play so pin is um you're establishing a contract you're promising that once you've placed something into a pen pin it will not move ever again this is not saying that you can't move foo future it's the moment you've given out a pin to a foo future that foo future will not move going forward so this is a it's a little bit important right if i am here in step zero then it's fine for me to do this right and then let g is z and let f is g and then pull f totally fine because at these points i haven't called pull yet so i haven't given out a pin okay so let's talk about why this matters so with this type signature instead pull is now saying in order to pull you must promise that foo future will not move again and the reason for this of course is that we have references into the foo future once we've moved past step zero right once we start pulling then it is no longer okay for foo future to move because of this problem right now once pole takes pin look at how this changes so now if i try to write the same code so let's copy that down here okay so this is all still fine i can't call pole on f right i have to do something like x is pin new at mute f and now i can call f dot pole right and that's fine ir read is now going to point to the buff just like we did up here i can't do this though if i move well i can if i move um if i move f then i'm really just moving the pin right i'm not moving f and so this is still fine and i can still pull here so this is fine so what if i really tried hard to move f if i did something like mem replace and i did so how do i get at the f it's behind a pin if i really wanted to like i wanted the program to crash how do i do this well i really want to move the f behind the pin so how do i do that well you can imagine that i did something like i'm going to dereference the pin and try to get other thing inside it and i'm going to replace it with a new foo or something so will this work well let's look at pin so pin so this is the critical part this is basically what makes pin work dereff mute is only implemented for pin if the pointer is dereff mute so that makes sense right you can't you can't get to the the thing the pin is getting at without the type in between being dereff mute but also it only is implemented if the target of the pointer is unpin right what the hell does that mean well uh remember i mentioned at the very early beginning that unpin so this marker trait is generally implemented for all types it's an auto trait so you will get it for pretty much all your types if you're if all the things inside your type are unpin then your type will be unpin and most of the things in the standard library are unpin some things that are not unpin is anything that contains self references so for example foo future would not be unpin the thing that gets generated for this async f and foo is not unpin specifically here the compiler will generate something like not unpin for foo future so notice that this is opting out of the unpin thing what does that mean well it means that i can't dereff mute pin that won't work this pin to f i can't i can't do this this won't compile it'll tell me that the f i'm trying to get at isn't unpin which was required for the dereff mute on pin and so therefore i won't be able to get at this i won't be able to move the f behind the pin which was the whole purpose i have no way of invalidating this pointer and so let's look at how this changes the implementation of future the this poll so when you're given a pin to self you can no longer get at the fields right if i do match on on self dot progress all i get is a immutable dereff which seems like a problem right i can't i can't change the step by mat and this is because we've we are saying that we're not unpin so it's not okay to mutate self and then you can imagine that this is because he if i could just violate the pin if i was given um if i was given the ability to mutate self in here i could violate the pin contract immediately by saying like member place self dot buff with or just member place all of self with some new foo right this would violate the contract because we've given out a pin to this foo future so we are not allowed to move it all right this is still a lot to chew on and we will go through many more examples because it it takes a while for this to click so don't worry if this is if there's a lot here does that mean a big impact of compilation times not sure what you're referring to as you are stating that for each a weight point another step is created oh i see so it is true that the longer your asynchronous function is the larger a struct the compile compiler will generate for that code right because it has to keep track of all the progress points so to speak of that function um that doesn't really lead to longer compiles it does mean that the that async function is going to generate a larger future but it's not clear that that's really a problem how can you mute itself if you only have a pin right so that's the big next question um so let's deal with that so in the case of poll here we know that the reason why foo future isn't unpin is because moving the buff wouldn't be okay we know that it's fine to switch steps right going from step zero to step one is totally fine step one just has a pointer to buff so buff can't move so we can read we can basically reason about our future implementation or about what other type we have and say that in this case i know that it's okay for me to modify self dot progress it's not okay to modify anything else but it is okay for me to modify self dot progress and so this is why for here and this so you'll notice that there are unsafe methods called get uncheck mute and map uncheck mute which are unsafe for this very reason right here so for example let's um uses get uncheck mute to demonstrate something that's wildly unsafe right if i did this so why is this not okay why is this unsafe because if i did this there's nothing swap there's nothing there's nothing stopping me from doing this right nothing stops me from doing this but this would not be okay right and then let's say i drop x here all of this is totally fine for me to do and here i've moved something that was behind the pin which i've promised not to do and the the reference to buffer inside f2 would no longer be valid so this is why this is unsafe because normally it's not okay to do in our particular case we know that it's okay for us to access self dot progress so what we can do is we can do something like progress is this right so what this unsafe is guaranteed is not guaranteed but with this what we have to promise in order to make this unsafe safe is that there are we're not breaking any references we're not breaking any self references by getting immutable access getting mutable access to progress here right and we know we're not doing this because we know that all the things inside progress refer to buff but there's nothing else that refers into progress so that's why this is okay and now of course here we can match on progress we can change progress um and that's all fine and this now would refer to and here we're mutating the here we're giving out a mutable reference to the buffer which is also fine because we know and the reason this is okay is because we know that the buffer won't move because foo future is is unpin now of course we couldn't actually write this code because you can't write self-referential code in Rust today but you can imagine that this is basically what's going on under the hood with when the async uh when the compiler generates a type from this is that it finds a way to get a mutable reference to this and assign it some lifetime um all right does that roughly make sense why we need the unsafe here if not I will happily explain it again and I just leave some time for chat to catch up um in particular you see how this this solves the issue um down here of now um there's no way for you to for you to cause the io read inside of the foo future to have an invalid pointer because it's hidden behind a pin uh can't the language team add reasoning in the compiler about internal references to avoid using unsafe yeah so um internally um you can think of this as the code that the compiler generates it knows when it can use this unsafe right it knows which things are referring to which and so this is why when you write async fn you don't have to write any unsafe but the compiler is going to generate some code that probably uses unsafe but it doesn't have to generate any unsafe itself so you don't have to write any unsafe when using async fn all right so that brings us to things that are unpin so one thing you'll notice is that if you look carefully at pin um you will see that there are let's look at this one maybe so we talked about get unchecked mute so this is you are promising right so this is where you see under safety you must guarantee that you will never move the data out of the mutable reference you receive when you call this function because that would that would um that would violate the invariance on the pin type right so that's what we said we if we do this then we will not move t that is what we're promising by calling this function or by wrapping this is unsafe um so if I don't use async I would have to use unsafe with pin ah no so not quite this is what we're getting to now notice that there's also this function get mute it goes from a self where which is a pin um a pin with a mutable reference to t and it just goes to a mutable reference to t why is this okay why is why is this unsafe but this is not unsafe and the critical thing here is where t is unpin so if the thing you're pointing to is unpin then it's totally safe to get mutable pointers to it this is the same thing that we saw below right there's an implementation of deref mute for pin if the pointer type so this would be like a mutable reference or something is deref mute to t and the target is unpin then you can basically just ignore the pin right if the target is unpin then it's as if the pin wasn't there why is that okay what's because if you have a type like uh if you have a type like if you have a u64 right or a u size or something then you know that a u size doesn't have any self references so you know that that type doesn't care about being moved so even if you've promised that you won't move it again it's fine to break that promise that is what unpin means if a type is unpin it means that it doesn't care about that promise so for example foo future is not unpin because it cares about being moved it needs to have this promise that it will not move once you call poll right however a type like um let's take io read io read is unpin because it doesn't care about you moving the io read you moving this type is fine even after you've promised you're not going to move it again it doesn't care about that promise so what does this mean in practice well uh let's go to also the there's something wrong about this line i'll explain that later um so if i do something like uh all right here's a type that you're gonna see in the you'll see in like all of the future the future's crate like i think it's in future's utile or something uh and that is the ready future let's just call it ready uh it takes a t and an e and internally actually it doesn't even do that it just takes a t and it has the value which is a t and there's an impulse t future for ready t this is a real future that you'll see in the future's library uh its output is t uh i'm gonna leave off the context just because it's annoying self output and what does this do actually i think this is like an option uh and this does poll ready self value take unwrap all right so this is the real implementation of ready basically um um so there are a couple of things you might ask yourself here um the first one is how is it okay for us to call take on this option take take some mutable reference to the option why is that okay well remember pin implements d ref mute if the target type is unpin and the target type here is self so the target type is ready so is ready unpin well uh actually in this case it's not unpin although it's unclear it matters um so in this case if t is unpin then remember unpin is an auto trait right so if all the things that you contain are unpin then you are unpin so in this case ready t is unpin in here because t is unpin so in that case pin implements d ref mute which means that we get a mutable reference to value which means that we can take it so ready here is really just like a future that immediately yields the value you give it and it is unpin which is what lets us basically ignore the fact that there's a pin here at all and this will very often be the case if you ever have to deal with pin of a type that where the type is something you have written usually that type will be unpin right usually you don't have to worry about whether things are pin or whether things are unpin or not because usually they will be unpin and then you can just use the value as if you had a mutable reference and so now the question becomes why is this okay well uh so r is a ready where the value is uh i don't know eight yeah eight you eight right and then i'm gonna call it r dot pole and then i'm gonna i guess uh pin you right because i need to pin it in order to call pole and in this case the unwrap is gonna fail but let's ignore that let's talk about the the pinning aspect of it if i now do um member place you are with another ready this isn't a problem right moving this ready even after i called pole isn't a problem because there are no self references here there are no references here that would be invalidated by moving the ready at all right this is just not a problem and the way we express that in the type system is that ready is unpin ready says i do not care about the promise you made for pin and so you can pin an unpin me however you want if you have me in a pin you can get me without a pin if you have me without a pin you can get me with a pin it doesn't the pin doesn't matter to me is what unpin says in fact we can see the extreme version of this if we look at pin here um where you say see that there's even a safe function on pin into inner which takes a pin to a p and gives you the p so just removes the pin right and here crucially the requirement is that the target is unpin because if the target is unpin it doesn't care about the pin so as long as you have unpin types nothing matters that or rather the pin doesn't matter and you see that this is explained in the docs too the requires that the data inside the pin is unpin so that we can ignore the pinning invariance when unwrapping it so the only time we really need to care about pin is when types aren't unpin because then we need to deal with this business of their self references that we have to be really careful not to not to violate if i understand correctly the only requirement to be unpin as it's an auto trait is to not be self-referential basically yes um now are there bigger requirements um the other time you will run into it is if you are generic over things um because if you are generic over things then they might not be unpin so this is why for ready for example we have to bound ourselves by t being unpin because otherwise we couldn't get a mutable reference they're ready because ready wouldn't be unpin um of course the example of this is imagine that uh t here was a self-referential struct then if you have a pin of mute of self then taking here would move that self-referential struct even though it was pinned and so that wouldn't be okay um can we construct a pin from an object or do we need some kind of pointer to call pin new right so that is that's an excellent question that's what we're getting it next so i i told you there was something wrong with this line if we look back at pin we'll see what it is um so pin new is totally safe but it requires that the target is unpin and why is that right it's because creating a pin around a type that doesn't care about pin is obviously safe right it's totally fine you can create pins left and right you can remove them and add them and whatnot however if you have a type that's not unpin really i want the docs to be like this if you have a type that is not unpin so this is the block for things that are not unpin um then putting it in a pin means that you are making a promise you are making the promise that you will not move it again that it cannot move again or rather that you will not move it again right and so therefore the constructor for pin when the type is not unpin is unsafe because you have to make this promise and if you look at the safety here there's sort of a a pretty long argument for why this matters so this constructor is unsafe because we cannot guarantee that the data pointed to by pointer so the the p type is pinned meaning that the data will not move or it's storage invalidated until it gets dropped if the constructed pin does not guarantee that the data p points to is pinned that is a violation of the api contract it may lead to undefined behavior and later save code by using this method you're making a promise about the deref and deref mute implementations if they exist specifically they cannot move so let's dig into why this matters right why is new unchecked a problem well imagine that I wrote a malicious implementation of deref mute so my evil pointer all right so uh this has a t and I'm going to deref for my evil pointer target is t all right so I'm gonna have a main here and I'm gonna do let x is uh so x is gonna be a foo future which we know is unpin right uh and then I'm actually gonna do my evil pointer and then I'm gonna pin that so this of course is not okay right because um you can only pin when the target of the pointer is unpin which foo future is not so we need to use new unchecked now let's imagine that new didn't have the requirement let's imagine that it was fine for the target of new to not be unpin right but we still have the requirement of unpin to get admutable references and such right so I still couldn't move out of x I couldn't I still couldn't move this because I wouldn't be able to do say mem replace uh this with this right this I still couldn't do because this pin wouldn't implement deref mute because foo future is not deref mute right so why isn't this okay why can't pin new just work for any type why does it need to have this bound on unpin to be safe so the reason is uh imagine that this code all imagine that um that pin new accepted any pointer type it did not have any restrictions on um on unpin then notice that if I do uh notice that the deref mute from my evil pointer it is given a mutable reference to self there's no pin here even though down here x has been pinned right this deref mute is still given a mutable reference to self so if I had this evil pointer we're inside of here I'm gonna replace uh let's say like t is default or something it's not important or replace it with something here then now this line violates the pin guarantee right this wouldn't be okay and I think they get at this here um yeah um by using this method you're making a promise about the deref and deref mute implementations if they exist they must not move out of their self arguments right pin as mute and pin as ref will call them on the pinned pointer and if you look at um as mute uh so as mute goes from so this is critically the reason this is necessary there's this function that's it's easier to read it here so what does this function do well it lets you go from say a pin box t to a pin at mute t so as that useful what is useful for cases like you want to call future poll so future poll if you recall has a type that requires a mute self so if you have like a pin box t and you want to call poll use as mute to get a pin mute t right so this function is pretty straightforward but it is safe for all types right so it internally basically just calls self uh deref mute pin new unchecked internally this is what it does right and why is that okay well because uh if I have a pin to the outer to the to the box t then the target is also pinned like there this should be totally safe but the exception to that is I have to go through deref mute and so if the deref mute did this this would not be okay and so this is why you creating a pin in the first place you are making a promise if the type is not unpinned you're making a promise that the deref mute doesn't abuse the power it gets from getting mute self in fact we have a similar problem with drop right so um so recall that the the guarantee we give when we put something in a pin is that if I've ever given anyone a pin around a given type I can never move it again I am not allowed to move it again at no point in the future if I've ever given out a pin I can't basically I can't undo a pin promise unless the type is unpinned but when I drop a type drop is given a mutable reference to the type even if I've previously only had pins right when the pin is dropped then the pin is going to call drop on the inner type and that drop is given a mutable reference to self so it can move out of a thing that had a pin around it not okay and so this is why you'll see in the documentation for pin up here um yes to see they have this like big example about self-referential structs um so here they talk about this thing called the drop guarantee and so concretely for pin data you have to maintain the invariant that its memory will not get invalidated or repurposed from the moment it gets pinned until one drop is called and specifically there's also requirements on the drop implementation right if you if your type use is pinning you have to be careful when implementing drop because the drop function takes mute self but this is called even if your type was previously pinned right so this is the issue we just raised and exactly like this it's as if the compiler called get unchecked mute for you which is unsafe so um if you've ever given out pins to your type right if you basically think of it as if your type ever implements something where it takes a pin self then that means that your drop implementation you must treat as if you're implicitly taking pin mute self and so they give this example of how you could do this safely which is uh just in drop pin yourself and only then do your uh write your drop implementation yeah that makes sense because otherwise you could do all sorts of bad things in here which is not okay uh I don't think reddit actually needs unpin for t don't think that's true why do we need a wait can't compiler automatically do a wait when function called is async well okay so if if you only ever wrote code like this if you didn't write the await keyword then um it would mean that I would have no way to do something like uh oh this is a bad example but sometimes you want to wait on multiple futures so this is often referred to as a select like I have two futures and I want to wait for either of them to complete I don't want to wait on them in sequence I want to wait on them in parallel um and if the compiler automatically awaited for any future it saw then you wouldn't be able to do that um uh what is the context behind this what is pin and why do you need it right so this is what we've sort of been discussing in the past but I think it's useful to summarize it at this point so why do we need pin we need pin because some types uh are not okay with being moved right at some point in their execution they're going to take a reference to something inside of themselves and if the type was moved that reference would become invalid which would not be okay it would cause undefined behavior and so pin is a way to to fix this which is if you ever give someone a pin of a type then you are making the promise that it will not move again and they can rely on that promise right so for example foo future relies on the promise that it will not move again to create a self-reference if it didn't have this promise it couldn't safely do this because if it were moved this pointer would become invalid uh how do you deal with panicking then I thought that it was calling drop on remaining memory uh so panicking will also drop types but the the requirement is still the same right the requirement is still that it's going to call the drop method on your type and whenever the drop method is called on your type then you need to make sure that it doesn't invalidate any pin requirements you made in the past so let's um maybe this will help um let's go farther down here um so let's say I have a stark foo and I implement drop for foo here do I need to care about pinning no because at no point have I done anything that deals with pinning right what about now if I have this do I need to worry about pinning and drop no because foo is unpin so it's always safe to go from a pin of foo to a mutable reference to foo and so drop still doesn't have to take any special considerations what if foo internally contains a foo future so now foo is not unpin I have at least one method well actually how about now do I have to do make any specific special considerations and drop no because at no point has there been created a pin of foo or if there has I haven't taken advantage of it so that promise may or may not have been made to me but I haven't used that promise in a sense and so I know that I can't be violating any invariance that I have made um around foo future because I haven't used it it's only if you have a type that is not unpin and that type is ever placed in a pin that you use so basically if you ever take advantage of that promise right it's only if you rely on that promise that you now have to be really careful in drop and so the recommendation that they make in the docs is basically that you do this you write this inner drop function which takes a pin to self and then you give it pin of self so you basically pin yourself before you call drop on yourself you have a specialized version of drop so really the solution to this would be for drop to always take pin self but of course they can't do that because the drop that wouldn't be a backwards compatible change right the drop trait has already been stabilized for a long time uh you can do impulse t unpin for ready let's see for ready yeah so here I could impulse t unpin for ready impulse implementing the unpin trait uh is totally fine um actually you're right you're right uh so the reason this is okay okay let's uh I don't want to keep all the other stuff um but let's try to actually compile some code just to see why this matters this is actually an important point so let's uh let's deal with it uh use standard future future task context uh pin see that we can actually compile this code uh this technically takes a mute context this um all right so this code compiles right uh there's no unsafe in it um and so you might wonder well why is this okay right why is this okay well remember that the pin promise only matters if you make it so here we are moving t right we have a pin to self and we're moving t why is that okay well it's okay because we've never given out a pin t right this t that we contain we've never given out a pin to so we've never made the promise that we'll never move it and so therefore it's fine for us to move it here yeah now this might make you feel a little bit worried why is this okay right why can we why is it not unsafe to implement the unpin trait and it's because this implementation in and of itself is not a problem it is only a problem if you ever write unsafe code elsewhere so specifically um this implementation of unpin is totally fine right it's more if we now tried to give out a mutable reference to t right if i hear tried to do something like uh pin you mute self dot i guess self dot imagine that i wanted to write this right this won't work why won't that work well okay let's say that t is future right i just want to show you that this doesn't compile for the right reasons so you'll see that here it's saying that i can't call pin new because unpin is not implemented for t right so the only way that i can pull this value is i need to have a pin to it and so i would have to call pin unchecked and this is not okay right so this unsafe is not okay because t isn't unpin so even though we have this impulse this impulse only problematic if we have unsafe code elsewhere and so that's why that's why we don't need the unpin bound here is because we never give out a pin to t and so therefore moving t is fine yeah all right so we've we've talked a lot about the sort of theoretical underpinnings of pin and unpin um so let's look at some interesting cases from the standard library i won't actually we can talk about actual fields so the documentation spends a lot of time on structural pinning and the basic idea here is imagine that you have a wrapper type um so i want to write a uh struct my future and it's just going to hold an f and i'm going to input future for my future where f implements future just write out the signature here so what do i write here so notice that my future here is entirely unnecessary right all it's doing is forwarding the future implementation uh to the underlying future that's all it does but in this case uh here we have a an example of structural pinning and so basically what structural pinning means is that if you move the outer type the field also moves if you move my my future then f will also move so structural pinning means so in this case we have a pin to self and uh we want to call self dot f dot poll right this is what we want to write what will the compiler say it will say uh it will say there's no method named poll find for so this is a very unhelpful compiler error um basically it's saying i can't find this method poll you're talking about and the reason for that is the poll method is defined on pin of a type not on the type itself okay so pin new and we always we always try pin new first just to see whether it work um okay so now we found the poll method but it's saying that the trait bound f implements unpin is not satisfied right so remember pin new in order for it to be safe it has to require that the the type that's pointed to is unpin okay so why doesn't this work well we could of course say we only support forwarding to futures that are unpin okay so that compiles why does that compile well if f is unpin then my future is unpin so self is dref mute through the pin right the pin dref mute sort of enables when the target type is unpin and so now we can get to the f and we can call pin new because the target type is unpin because the target type is f and so therefore this code networks but this is a little sad this means that you couldn't do something like have a main uh let's do an async f and foo i couldn't do this right so here i'm trying to set f to be some async block where presumably i'm gonna write a bunch of code in here right and then i'm gonna await that future this won't work why is that well uh we want there to be an implementation of future for my future right because otherwise we can't await it we can only await things for futures and for that to be the case um for this for future to be implemented for my future then we've already said that we require that the f is unpin right and so we need the gen so gen future here is the the future type that the compiler generates for async blocks and so okay we need gen future of t where t is like the actual contents of the async block uh we need that to implement unpin but that requires that this so see how it says static generator at source lib rs line 20 right so what's at line 20 at line 20 is this async block so it's saying that uh the bound gen future implements unpin right so this async block implements unpin is not satisfied and so now we're saying that this async block isn't unpin because no async fn's or blocks are and therefore my future doesn't implement future and this is a little sad right we would really like to not have this bound we would like my future to forward any future because why not so how can we write this code so that we remove this bind bound well we're gonna have to use new unchecked right because our alternatives are either use new and require unpin or use new unchecked so how do we know that it's safe to get access to a field well on pin there's map unchecked mute uh there's also get unchecked mute but let's use map unchecked mute first um let's look at what it actually does um so map unchecked mute goes from a self so that is a a pin with a mutable reference to some type t and it takes a function that goes from mutable t to mutable u and then returns a pin of a mutable reference to you right so this is going from your pin of some type and you have a thing that converts from that type to some inner type and then it gives you a pin of the end result and so why is this so uh in our case what we want here is this is basically a reference to self so let's call it this uh and we're gonna give a mute to this f okay let's first see whether that compiles okay that compiles we don't need this anymore uh let's make this pub so it doesn't warn us about it okay so that compiles but is it safe right here we have an unsafe block what does that mean well map unchecked mute says the following this function is unsafe you must guarantee that the day you return will not move so long as the argument value does not move and also that you do not move out of the argument you receive to the interior function okay so there are two things that can go wrong here the primary one is that here self is a pinned mute self map unchecked mute gets a mutable reference to self right so in here there's nothing stopping us from doing uh moving self which might not be okay right it might not be okay to move self because f might be unpinned so this is not okay so this is one of the reasons it's unsafe the other is if uh if map unchecked from you returned a reference to something that might not move uh that might move anyway but let's ignore that for a second like if it if it like dereference the raw pointer or the point of some other thread or something silly right like who knows what this does it could take a raw pointer to somewhere on the heap that it just like generated randomly and that would obviously be unsafe um but ultimately we need to figure out why this line is okay or whether it's okay and so here what we're saying is that first of all our closure here does not move things that aren't done pin right it doesn't move anything in fact and second of all the guarantee we need to make is that as long as this doesn't move this won't move and that is the case right we don't move f we never move f and as long as my future doesn't move right which is this then f will not move yeah and so this is because here we can rely on this thing called structural um what do they call it structural borrowing structural structural pinning um basically they're asking whether pinning is structural for a field and in this case f uh f is a structural field of my future because if my future moves then f moves if my future does not move then f does not move and so it's okay for us to make this promise because we have been promised that we won't move therefore we know that f won't move and so therefore this unsafe is okay yeah um it's a little bit complex there are a bunch of different ways you could write this code this is the one that uses the least unsafe right map and if you can use map and check mute you should because there's less opportunity for accidentally abusing it one alternative here right would be unsafe get unchecked mute right and then we do something like uh this dot f dot poll but now we'd have to also do new unchecked to this with unsafe and also now someone could imagine this was in the middle of like a long complicated function nothing is stopping somewhere doing like oh i'm just going to set this to none or whatever and the compiler wouldn't warn them about this line because this line promised that the code beneath didn't do anything wrong and so this is why where possible you want to not generate these right or you could do something like uh f is and then this right that way that this wouldn't accidentally be used further down all right so if we go back to this now one thing we have to be really careful about is it's not okay for us to impel unpin for my future f here now right this is not okay why is that not okay because now there is nothing stopping me from writing this function why is it annoying to write this code um ah async fn bas i just need something to stick in here right i can now write this code there's no unsafe in this code right i create an f of type my future i pin it so i'm guaranteeing that it should not move again and i'm saying that f should not move again right so bas is an async fn so it's not unpin so i'm pinning it and that's totally safe and then i'm swapping out i'm just straight up swapping out um the my future in fact i could do dot f swap it out with a different buzz and all of this is safe even though clearly i'm breaking the pin guarantee right i made a promise with with this pin me pin you that i would never move f again and yet here i moved f again in safe code and there's a subtle interaction here the reason this is a problem is because this implementation of unpin is saying that you can move my future however you want but this unsafe is relying on the fact that if you have a pinned my future then it will not move this unpin is opting out of that guarantee for my future it is saying that you can freely move my future no matter what you like you can just move it freely and so this pin means nothing therefore this unsafe which relies on this promise also means nothing and so now we've broken this this is why this even though this implementation is not unsafe this adding this safe implementation breaks the requirements of this unsafe did that make sense someone asked why is it safe to ample on pin and it's because this on its own is never unsafe the only thing it can do is it can make things that are already unsafe be unsound right be incorrect so the when you write this unsafe um what you're really saying is i have thoroughly checked the code and i know that this is safe i know that this pin guarantee means that like if this will never move then this will never move adding this changes the guarantees of pin on my future making this unsafe no longer correct and but this impulse itself isn't unsafe because if we didn't have any unsafe code in here so for example if this was like plus unpin and let f so this is what we had before right this is totally fine now this unpin isn't bad so it's not really the unpin that's the problem so this is why it's not unsafe it's only that by adding it you are violating an invariant that other unsafe code in your code relied on why doesn't the compiler always do it the compiler can't add this impulse for you always because it doesn't know that you have this unsafe then relies on this not being unpin so always adding this would be wrong because if you have unsafe code elsewhere it might rely on you not having this impulse so therefore the compiler never adds it unless all of the unless your type is unpin if it's not generic or if all of the fields of it are types that are unpin then this unpin implementation will exist from the compiler all right so that should give us a decent view into unpin and why it's necessary and also how to use this with unsafe right so now if we remove this now you'll see that this code no longer compiles right i'm not allowed to create this pin new right the compiler won't let me create this pin new because my future is not unpin uh one thing that took me a while to wrap my head around the generic way to make your value unpin is to call box pin on it yes so that was what i was about to get to next so one thing you will see um and this might strike you as surprising which is why we're going to talk about it is if you look at box so box allocate something on the heap you will see down here this hidden in the darkness so take take a little bit of time to read this and see if it makes sense to you crucially why is this okay notice that here we're saying that for any type no matter whether it's pin whether it's unpin or not unpin if you put it in a box it is now unpin or rather the box of the type is unpin all right so let's dig into it why is this okay well this is okay because if you have a type that is on the heap right so for example if i have here box of f then if you move my future then f is still not moved right so now you can move my future all you want i can still always keep pinning it if you pin my future and then unpin it and then pin it again not a problem all of that is fine because f still hasn't moved so it's not because it's not self-referential right what um what unpin for box is saying is regardless of whether t is unpin or not unpin it is unpin when it's inside of a box and this is because now the structural guarantee is very different so can we now do this right so so by making this box given the implementation we just saw this means that my future is now unpin regardless of what f is right because it was a blanket implementation so can we now do this right now that self self is now or pin of self is now going to implement d ref mute so we can get at this does that now work we get rid of this broken thing down here this is now pile oh i need to um oh that's fine let's wait a second with that too so i can now correctly get to f uh and in fact i can even create a pin to it but really what i want here is this right i want a mutable reference to the target of the box to the f if i did this i would just get a mutable reference to the box which doesn't really help me i want a mutable reference to the target of the box and then i would pin that because i want a uh ref mute f right that's ultimately what i want to call pull on so does this work no because unpin is not implemented for f so notice that um boxing in f just means that my future is now unpin it doesn't really solve this issue we still have this this requirement that we can only guarantee that f won't move if we won't if the box uh how to explain this um okay so why doesn't this work well i can write a function that does this pin uh let's x is box new async right so this is not unpin uh mute x i need my async pass back uh right so a box of f makes the the box itself is unpin but we still that that's not sufficient for pin new to be safe on it because otherwise i could write code like this right where i still get to move the thing that's behind the box so boxing something in and of itself doesn't really help with pin it all this is saying is that if you have a type that contains a box t then that type is now fine to move even if you made guarantees about pin if you've made guarantees about pin of t on the other hand then box won't help you right specifically in here we want a what this code really wants to produce is it wants to produce a pin uh mute f right but in order to give out this we need to give guarantees about whether f will move and so it doesn't really matter whether my future moves right all this box is saying is that if you move my future that does not necessarily move f but that's not saying that f won't move which is what we require here we require being able to say that f won't move and so in fact if you want to use box this way then what you can do the story here is a pin of box of f now what we're saying is that if my future moves then f won't move and a pin of box of f yeah so a pin of box of f you won't be able to get a mutable reference to f through this so you won't be able to move f so if we know that my future won't move then we know that people want to like switch out the f that's fine but if we now want to get a mutable reference to the f uh so let's first let's see with this compiles uh box does not implement structural pinning yes that is one way to say it that's right uh yes we still can't call pin new right and the reason for this is uh if we if we could just get a mutable reference to f this way uh then anyone else can as well and they might just like replace it entirely and so we're gonna have to use new unsafe new unchecked just give me a second there we go so we still need this unsafe right even though now this is no longer the no longer structurally pinned right if you move my future then box will still now move tidy this up a little all right so we still need this unsafe map unchecked mute right and here what we're guaranteeing we're we're still saying that if my future doesn't move then f won't move so this is still the same guarantee that we made before however now my future is unpin right so you can freely pin and unpin my future and that's fine but this guarantee can now be violated pretty easily imagine that i wrote some code like this not knowing about the stuff above right unsuspectingly i didn't really look carefully through the file and i wrote this implementation well this is now a huge problem because now i can do this my future f of this and then i pin that just give me a second to tidy up this so we can oh get f does i get mute on pin as well right so now i've written completely safe code that moves the f even though we pinned my future this is not okay right imagine here that i did like x dot pole and then i did x dot pole here again uh yeah right this is totally safe code it calls pulse it gives out pin but now this unsafe is being violated why is that well this unsafe is saying if you've pinned self then it's fine to give out a pin to the f but that's not true right because my future is unpin and so now you can write this function totally safely and this writing this implementation is going to violate this unsafe because this is giving out a reference to the to the to the f seems like if there was a kind of reference that is like mute you can't use memory place on the like on it then things would be safer so that is what pin is right if you put something in a pin you cannot get at the underlying contents mutably unless you promise you won't move them it just that it turns out that there are a lot of ways to move things and just saying like you can't do memory place isn't really sufficient um the the standard pin docs as a bunch more examples of how you might accidentally move something um but just saying no memory place is not going to solve it it really it really is you cannot give out mutable reference to things unless they're wrapped in a pin so pin is exactly that promise um and that has to rely on things like unsafe right remember this doesn't have to be memory place this is the same just assignment memory place is just a convenient way to write it um okay so this is a problem so how do we make it so that people can't accidentally write this code well we can write a pin box f here so there's a box pin which returns a pin of box and now you see let's get rid of these because they're not important and see here right so now this function you can't write safely anymore because what we're saying here is we're gonna put f in a box and then we're gonna pin it we're gonna say that uh this is going to be used as pinned and so you can't just like arbitrarily give out mutable references to it and so now this get f if you try to write it you would have to write unsafe code which wouldn't be safe right so this is now no longer possible and now it's going to complain about uh this get f right so this this you can't call anymore so that's great um and in fact let's just comment out this whole thing we don't read it needed right now what you know terminated it's terminated right there oh um and now look at what we can do so so this compiles and there is now no unsafe code here whatsoever so how did we do this well the box guarantees that moving my future will not move the f the pin guarantees that there is no way to move the f anymore unless it's on pin right because it the pin pin wouldn't implement d or f mute to the f and you can't so that means you can no longer get a mutable reference to the f unless it's on pin which means that pin box is this neat construct that um you can use to take things that are not unpin and make them unpin and the reason is the box makes sure that it will never move right the box makes sure that it has a stable location so to speak and the pin makes sure that you will never be able to move f unless it's unpin so in general pin box is the way to go from things that are not unpin to things that are unpin of course the downside is now there's a heap allocation here which may or may not bother you um but this is such a useful construct that if you look at box you will see that box has a function called pin you can give it a t and notice this is any t it's not bound by unpin at all and it gives you a pin of box of t if t does not implement unpin if it implements unpin you could move it just fine right because the pin is going to implement d ref mute but if t does not implement unpin then x will be pinned in memory unable to be moved right the box is going to put it on the heap somewhere and the pin is going to make sure that you can never get a mutable reference to it or you can never get one um safely of course unsafely you can do whatever you want yeah okay so let's look at the implementation of this because our intuition here is that uh it's actually a little bit subtle why a pin box is safe to construct for any t so let's look at what internally what it does so there's a function into pin that takes a box t in fact we can look at this here first uh into pin so this takes any box t and gives you a pin box t and remember anything that's wrapped in a pin comes with the promise that the target type will never move again right so how can this function be safe right this is what we just discussed it is safe because the box makes sure that it has a stable address and the pin makes sure that you can't get a mutable reference to it anymore and you will see that indeed internally it is it uses unsafe it says new unchecked it is not possible to move or replace the insides of a pin box t when t is not a pin so it's safe to pin it directly without any additional requirements and this is basically the the the short version of the long roundabout way we've we've got at this now yeah okay so writing all of this unsafe code should make us all feel a little bit uncomfortable right um so let's see if there isn't a better way uh let's sort of save this by putting it in a comment because that's how we save um so let's go back to what we had where we did uh let f is unsafe self uh map unchecked mute this this dot f f dot cx and we have this async foo that's going to use my future right so this is remember this is how we wrote it without having to use box uh and it required this like one line of unsafe but ideally when you write code like this you don't really want to have unsafe if you can avoid it and so this is really neat library called pin project um which basically takes away much of the pain of dealing with this these invariants on your own so let's go to our cargo tumble and let's add pin project equals zero four zero alpha eleven i want a bunch of stuff all right so what does this let us do well pin project uh let's look at how you use it first and then we'll look at how it achieves what it does so you add an annotation on a struct and you say which fields you want to pin and then in here you do self dot project dot f dot poll cx so notice that i added some annotations and then i replace the unsafe code with this project call and it compiles there's no unsafe code right no unsafe code and we get the same thing that we got before now so that seems really neat it would be great if we could just always do this and in fact pin project you can i've used this in a lot of situations already but it's useful to dig into what does it really do under the hood and also you'll see something weird here like let's say that this had a t and this also holds a t notice that we've not added the pin annotation to the t let me place this over here uh let me just build up a little bit of an example here my future f t uh of f m bar so this is gonna create a where's my example of this down here uh x dot get t the t is gonna be uh i don't know in fact t is also gonna be something that's on pin sorry give me a second i will explain in a second i just need to write some code to talk about first in fact i don't even need this code before that's annoying fine because the ref that's not ideal i should file that as a bug all right fine not worth it fine um in any case uh if we have a field that is not marked as pin in fact fine fine fine fine fine fine fine fine then let's do this just to demonstrate what happens i can do this dot t is bar and then i can do this dot f dot pole fine uh i don't have a type to use here you can mostly ignore all the various back and forth i'll explain at the end there we go again i would convulse great um so notice here that i have a t that is also totally unbound right it may be unpin it might not be unpin um and inside of pole i'm allowed to change t even though i have a pin mute self and this f so remember this in order to call this i need one of these in order to call this i need one of these yeah and that works right this compile just fine if i try to do this uh if we made this also an option which is a little bit annoying but uh some to this this requires a mute f and this will not compile right it's saying you have a pinned reference to f so you can't do that so pin project has actually generated a thing where i can safely touch i can so safely mutate fields that are never pinned right because remember the pin guarantee only matters the pin promise only matters if you ever give out a pin to that type here we never give out a pin to that type to t right and so it's totally fine for us to modify directly however we do give out a pin to f and so mutating f is not okay but getting a pin to f is totally safe right because we've never given out a mutable reference to it and so therefore this code is fine and so pin project pin project really gives us exactly what we want if we go back to the the ready example from earlier right remember how we had this whole business right if we remove this unpin then now this won't work right because uh t isn't ready isn't necessarily unpin so pin won't be deref so here one thing we could do we can pin project this we can we never need to pin value right we never need to pin the t and so now pin project adds this project method here and now this will just work so pin project is actually also going to automatically add an implementation of unpin for ready that is going to be guaranteed safe so if we now do cargo check uh i guess this still doesn't work right because it's not okay but this compiles and notice there was now no need for any unsafe for ready either and we didn't need to write the manual implementation of unpin even though ready will now in fact be unpin so pin project is really handy for this but how does it achieve its magic right that's the real question here um and so in order to explain that let me first undo this like option f business because it's a little annoying let's do cargo expand so cargo expand is this really handy uh tool for cargo just a cargo install cargo expand that lets you show the expansion of all macros so let's scroll up here a little bit and see what we got generated okay so we have my future so we recognize oh man uh so we recognize my future um and you'll see that it generates this unpin scope so this is just so that it doesn't pollute the type space with lots of names and there's some really like subtle stuff going on here but basically what it's doing is it's generating a type called my future projection and for any fields that are pin it's going to hold a pin to a mutable reference to the field and for anything that's not marked as pin is going to hold a normal mutable reference to the field right so notice here f was marked as as pound pin and so therefore it the mutable reference to it is a pin and t was not marked as pin therefore we get a normal mutable reference to it and then this project method is unsafe uh you notice that it doesn't get unchecked mute to self which gives it the underlying future and references to all the fields and then it pins the fields that are marked as pin and does not pin the fields that are not marked as pin and based on what we know from before why is this okay why is this unsafe block okay well as long as we never move self which this does not do and we never give out mutable references to fields in self that are pinned right so f here is something that we pin there is no way through what pin project gives you to get a mutable reference to the f safely and so therefore we're not and we're not uh invalidating the guarantees required by new unchecked and so therefore this code is safe giving out a mutable reference totally fine because we never give a pin to it or pin project never gives us a pin to it if we wanted to create a pin to it it would either need to be unpin or we would have to write unsafe code um and there's also so this little bit nasty thing here this is basically the impulse of unpin from my future right so it generates an implementation of the unpin trade and it does some magic so that um in our case we will be unpin as long as as long as f is unpin right remember that the reason why it was okay for us to have a blanket implementation remember back here we had an impulse unsafe uh t unsafe for ready t right remember this was totally safe uh this should say unpin and the reason for that is because we never give out a pin to to the t and so it doesn't matter whether t is unpin for whether ready is unpin and that is what um it generates here so it generates this this intermediate struct that any fields that we any generic fields that we never give out a pin to we don't care whether they're unpin for the purposes of whether the wrapping struct is unpin whereas we do care that my future is only pin if is only unpin if f is unpin it doesn't matter what t is and that is what this generation does so pin project will give us um safe pinned references to any fields we need to be pinned self safe mutable references to any fields that are not pinned and a correct implementation of unpin for our type even when it has generics um it also does the same for uh for like uh enums where you have pinned fields and variants and stuff um the other thing that's sort of neat uh that actually pin gives you is um if I have and this might strike you as weird remember this f here so this dot f is a pin mute f right I have a set method that lets me do this I'm gonna have to make this an option again as pint mute that's funny and projects all right so this might strike you as funky right so this f is a pin of f and I'm allowed to change it so remember how we said you should never be allowed to change to move something that is behind a pin unless it's unpin well f here is not unpin there's no unsafe code here and yet I'm allowed to change it to none and the reason is because you are never allowed to move it until you drop it and setting is totally fine I don't give out a mutable reference to the f it's not going to get moved it is only going to get dropped because I'm gonna replace it with something else and I don't get back the old value and so it's never moved it's just dropped and the new thing is set in place so this actually this actually works totally safely uh trying to catch up in the middle of the video is hard yeah I believe it this is a a deep a deep uh like nest of things uh are we talking about pin as in circuit board no we are not talking about pin as in circuit board we're talking about pin as in uh the type used to give guarantees about self-referential structs in rust um so a very different type of pin uh yeah so pin project is really neat for these types of things um it handles basically every use case I've thrown thrown at it uh and the author is also very receptive to um to bug reports and such although I've found relatively few and so this is a very nice way to deal with pins to deal with pins without having to write unsafe code and this has nothing to do with futures really right like you can use pin project for anything that requires pin it's just that currently you don't really require pin for many things that are not futures because most things are unpin unless they happen to contain async fn's which are futures um now that said these are things that might change so for example um if rust gets generators so generators would be something like I don't know what the syntax will be but like numbers um so this is something some other languages have um where you could imagine probably something like this so the idea here is that this would um constructed an iterator right so every time you call it it continues from the previous yield point and so it needs to keep any state so for example it's the exact same thing right I have a buff of like this and I'm gonna like I don't know man like buff I'm gonna yield like buff this or something something like that so here this generator which is an iterator uh is now not unpin because it has self-references and this has nothing to do with futures but this will still require pinning because it's any self-referential struct and here you can see that this is the same as what we've been talking about before if you remember at the very beginning uh we also talked about this parser type see if I still have it here somewhere no probably not um we talked about this parser type that once where you have a parsed representation of a byte stream so such a type would also not be unpin so if you think of a type like um uh let's do docs rs uh oh that's that's weird um um owning ref so the owning ref crate it basically gives you self-referential structs um I don't know if they have a good example yeah so the idea here is that you can return something that has a reference into itself the owning ref crate lets you do this um and currently they do this with a bunch of hacks through the type system and things like stable address traits and such it is pretty reasonable to assume that this could be represented through pinning um it currently isn't but here you sort of want to imagine that this type now requires that it's pinned otherwise the self-references would no longer be valid uh Mozilla has a nice explanation for iterates and generators with JavaScript yeah I mean generators are just yet another example of where you would need pinning because they're they so easily get self-referential so currently this is also worth knowing um any async block in any async function is marked as not unpin even if it isn't unpin sorry even if it is unpin so for example uh our little boss down here this has no self-referential fields there's no internal pointers here right it's empty um this the return type from this will still be not unpin even though it doesn't need to uh I don't know if this is something that'll change it'll it's probably pretty difficult to figure out whether a given generated type um should or should not be unpin and getting it wrong would be disastrous um and so my my guess is what we'll see is that these will all just remain uh not unsafe when they're generated okay so the the next and probably last thing I want to do uh is look at some changes from the old future stuff which didn't require pinning to the new future stuff which does require pinning um in the context of a particular library that recently made this move so we'll look at basically some of the prs um so that's what I want to do next but before we do that are there any questions about the stuff we've discussed so far so now we've gone through basically all of the all of the underlying theory um and hopefully it's it's now clear what pinning is needed for and what unpin is needed for um I'm not going to deal with things that are off topic right now um just because this is a sufficiently complex topic that I don't want to veer too far from it uh we can do some q and a at the end maybe so probably when we demonstrated examples of code that is not okay by using memory place we needed to call poll on the returned value not on the new value um yes you're right uh the poll would be on the actually they're they've both been moved so they're both been validated but yeah I mean the problem doesn't arise until you use the pin again probably um but nonetheless like you shouldn't do it in the first place um if set is okay yes the reasoning why set on pin is okay is a little bit subtle but the basic idea is that set doesn't allow you to move anything right nothing moves when you call set or rather um the thing that was in the pin gets dropped and the thing that is not in the pin and it gets moved into the pin is moved into a pin so it's now pinned so regardless of whether it was unpin or not unpin it is now pinned and it wasn't before because we were given an owned version of it right so that that that's sort of the informal argument for why um why set is okay all right so let's look at uh some actual changes to use this and you will recognize a lot of the patterns that we've built up to uh so far in those changes so uh the library we're going to look at is a library called tower so tower is basically um it's an api for services uh like network services but all sorts of services uh in like uh like the idea is basically anything that goes from any function that goes from a request to a response is a service and then it defines various uh both traits and um help instructs to manage these like rate limiting and logging and all sorts of things um it's not terribly important what it does but it is a library that had a bunch of code that was written for future 0.1 uh and that has recently been converted to using standard future uh the question was if there's a difference between set and replace where the return value is immediately dropped um i think those are equivalent but i don't want to say for sure actually we could look at this so um sorry for the slight uh here set source yeah so set is this is basically a memory place it's just setting the target to be equal to value which implicitly drops the old value which i think is equivalent to memory place uh or rather it's equivalent to memory place where you immediately drop the return value i think um so a warning for those of you in rooms with uh like where it's all dark now uh my github is light so this will become very bright for you um all right so as you can see from the the close pull requests like a bunch of these are update like particular module to standard future so let's take a look at some of these let's start with some of the early ones uh let's start with like tower load um so first of all this bun this bumps a bunch of the dependencies to use uh the future's core preview and like tokyo and tower previews notice that it also uses pin project because it's handy uh and so here we have a bunch of types and some of them we never give out pins to right so we add pin project to them just so that we're able to mutate their inner fields even in a pin context right so we might in fact if we scroll further down you'll see here that we want to we want to implement the no it's a bad example um well actually i can explain it without that there are cases where you have a pin to one of these and you want to mutate either inner or load and mutating either of them is fine because we never give out a pin to a t or a pin to an m and so therefore we sort of basically we want to implement unpin for this type but if you recall from back when we were when we wrote the um the manual impulse of unpin for ready it's so easy to like someone will write an unsafe block somewhere else in the file and that causes it everything to be broken because of that impulse line so this is why i prefer even in those cases to add pin project to it because that will guarantee that no one will break it later some of it is just adding these context things that are now required you'll notice that service in fact this might be worth talking about separately although it's a while ago so if we look at docs tower service this is an interesting trade-off the the tower service trade is sort of the core of tower and it has two methods it has pole ready and it has call now pole ready is going to be called in an asynchronous context right so it's going to be called from a future basically but notice that it takes a mutable reference to self it does not take a pin to self um and the reason for this is because call which is used when you want to issue a new request we really want call to take a mutable reference to self because otherwise you would have to like pin the service every time you wanted to call something on it which effectively just requires that the service is unpin and so taking pin here and not taking pin here is pretty weird right because you're going to be basically alternating between calling these two different methods right you're generally going to be calling pole ready and then you're going to be calling call and so if we mark this as taking pin self and mark this as taking mute self then now that is effectively the same as saying that services all have to be unpin because otherwise once you have a pin self you wouldn't be able to get a mute self unless self so the service was unpin similarly if we made and so this is equivalent to saying that service must be unpin but just without have requiring the user to write like pin you in the first place we could make both of them take pin mute self right that way pole ready would just be pin is normal and call would now require you to have a pin but that makes it a little bit awkward too because it means that first of all anyone who wants to use a service needs to pin it before using it and second of all anyone who implements the service trait would have to deal with the fact that call can't mutate self like things inside of self so it just makes the API a little bit more awkward like implementers would have to use pin project a project and callers would have to pin things but this is actually an ongoing debate this is part of the reason why tower the tower on standard on standard future is still in alpha I think there's an open issue tracking this basically where here so I open this this is actually partially in preparation for the stream trying to figure out why is it shaped this way and what is what are the tradeoffs and the observations here so option a is we keep things the way they are option b is we add an unpin bound to service right because requiring these to both be mute self even though this is in an asyn context basically implicitly requires that the services unpin so we could just make that requirement explicit we can make pole ready take pin mute self and then call take mute self just to make it even more explicit but then we still that's effectively still the same as making the service unpin or require the services are unpin we could take everything make everything a pin but now the apis are less ergonomic so there are tradeoffs like this that affect real code bases and if we look back at tower load you'll see that at least for the time being they both take mute self um let's see if there's anything else you'll see here he's another like this is a similar to my future right where we have a thing that internally contains the future and then it also has some other things so in this case we use pin project on it we pin the future because we know that we're gonna have to we're gonna need a pin of f in order to call pole on on the future but we want to have mutable references to both handle an instrument because the code if you see here the implementation of future for it um it is going to modify handle and use a mutable reference to instruments we want to have mutable reference to both of them but we also want a pin to the future which pin project all gives us and you see this pattern just repeat over and over and over again right pin project only some of the fields are pin uh and those are the ones that we're gonna need pins to and for the tests what this means is that for the tests we can now use async blocks right because they generate things that are not on pin but because all the methods now take pin self it's all fine um you can see also in this so here we had a trait that um so this trait discover so the idea would discover is that it's a trait that lets you discover new services and it has a pole method sort of like future pole except and sort of like stream right so stream is another example of a trait where um so stream this trait has pole next and it also takes a pin mute self because we want to be able to support implementations of stream that are not on pin because they might internally contain like an async block or an async function right you can imagine a stream for a stream wrapper that um takes a function that produces futures and just keeps calling it and keeps producing the thing that it gives we're gonna have to pin the resulting futures and so stream pole next takes pin in general basically any pole method any method you see that starts with pole is going to take pin of mute self and so here we have this this trait that used to take a mute self in the old future 0.1 world and in the new world it now takes a pin self in this context and that also caused all implementers of that trait so for example here we have service list they now all need to take pin self and you need to use pin project in this case um to access the thing that we don't even pin so the field isn't marked as pin so that gives us mutable references to it because we never pin it so we never give that promise um and here is an implementation of discover for stream so here we're gonna have to pin the inner stream and so therefore use pin project with pin um and that lets us project to inner which gives us a pin of the inner stream which we can then pull so in general for almost all feel free to look through these prs they're pretty straightforward um and see if there's anything you spot that's weird uh in general i think what really helps is um the pin projector is great i do think it's important to understand what it's doing under the hood and why what it's doing is safe which hopefully the explanation today has done um uh remember how back when we started talking about pin how um here so we talked about this drop guarantee and the drop implementation so you might wonder well what happens if you use pin project on a type that implements drop right remember how drop you you have this additional requirement that you don't move even though you're given mute self and drop and the way pin project handles this is if you try to use pin project on a type that implements drop uh then it won't compile so here let's uh get rid of this generator which doesn't work uh this still compiles great so imagine let me now do um impo ft drop for my future ft right so here imagine that i did something like a member place mute uh f as mute unwrap actually just f dot take what am i doing unwrap and then we did like i mean who knows what we did there right but here i'm gonna move f inside of here and this is obviously not okay right because we've given out a pin to f before and so we're not allowed to move it now well if i tried to do this this code will no longer compile this should say self dot f so see here that um the pin project uh attribute is saying conflict implementations trait my future must not import drop right we're not allowed to do this because it knows that if you tried to do this you might violate the guarantees and so pin project will not even let you do this and so this is pin drop thing where um if you do this if you really want implement drop for your type then you do this business is like a whole invocation you do future ft and you'll see here that basically what they've done is they've added their own trait pin drop and pin drop has a method drop that takes self by pin to mute of self just like the documentation for pin recommended right and now of course this won't let us mutably get access to f because the only way we can get f is through a pin so even if i did like self dot project here all i would get would be um i would get a pin to f i would get a i would get a pin of f which doesn't let me take right i get a pin option f which i can't take because f isn't unpin so project is saving me here and if we look at the the expanded code um up for let me just get rid of ready because it adds a bunch of noise so we have it up here so here's my future and you see there's an impulse of drop for my future which pins self and then calls the the pin to drop drop method on self so this is if you recall the exact same pattern that they recommended that you implement drop you immediately pin self and then call a drop that only gets pin self so pin project is here implementing that pattern for you and it's doing such a way in such a way that you won't accidentally violate it um there's also um they also have this project which is for accessing fields with invariants um and there's also unsafe pin when you want to generate an unpin implementation that is sort of unsafe according to the rules the pin project knows about but that you for some reason know that it's safe um so i highly recommend you take a look at pin project if you have to do anything that does with pin um there is also uh i recommend you read through some of the the tower changes because that they're almost just mechanical changes to move to pin very little bit has to do with like the implementation details of tower it's all about just moving things to pin so that might help just to get a lot of that repetition in um i also recommend that if you find old crates that were written for the old futures um like future 0.1 try to port one to your future 0.3 or to the standard future um some of it will be things that like traits have moved and stuff but much of it is just going to be dealing with pinning and now at least in theory you're equipped to deal with that and understand what the underlying issues are that you're you're working with um there is for example um there's one more thing that's handy to know about for the purposes of pinning which is the futures utils crate which has a bunch of like the basically all the things that used to be in futures like all the methods on future for example um are moved into this crate and one of the things that provides is this macro called pin mute um so pin mute is actually very straightforward but it's just a handy thing to know about which is um actually maybe i can demonstrate down here with yeah basically if you have something like this uh and you want to pin it you can do pin mute x and now you have x which is a future uh sorry is a pin of a mutable reference to my future it is basically equivalent to doing this um but it's a just a tiny bit nicer it avoids you having to write this code yourself um but that's also one to know about it's particularly useful in tests but at least now you know that it exists um and with that i think that's all i wanted to cover with pinning and unpin um are there any questions uh let's do questions about pinning and unpinning first or things you would like me to explain again this is like fairly technical content there's a lot of just subtle interactions um that can be hard to follow and even though like we've been doing this now for like almost three hours um it it is hard to put words to exactly how these indirect interactions work and so explaining some of them again might help uh i'll do off topic questions afterwards i do think that one thing you'll find with um with pin and unpin is it helps a lot to just to just use them a lot uh right like try to port something that needs pin try to read changes that are related to pin um try to read like the documentation of standard pin and see if you follow it and if you don't then like just keep at it because after a while it it clicks a little what what is needed and why but it just takes it takes exposure really will this stream be available when it's not live yes i put the recordings of all the streams on youtube afterwards i also if you want sort of a mental challenge so to speak i would say think really carefully about why remember when we talked about box pin like why is it okay for box t to implement unpin unconditionally um and why is pin box of t uh always fine to use like why does that give you pins of t no matter whether t is pin or unpin all right i think if there are no more questions about pin let's like pin that for later uh and do like general q and a i'll put so in the when i post the video i'll put uh time codes to when we cover the different parts of pin and unpin and also a pointer to um like basically now and when we do general q and a so if you have general q and a about rusts the ecosystem async me anything now's the time i'd like to start writing some cli tools in rust do you have specific crate recommendations for that well it depends what you're trying to write um i think structop is great clap is also really good um but i don't have any recommendations sort of beyond that it really depends on what you want your tools to do um see those one further up here too uh this is neovin it's not emacs and it's not i mean it is vim but it's not vim it's neovin uh and i have someone asked me about my desktop environment earlier i have a separate video where i go through like what my desktop environment is like and how it's configured and all my configs are online um literature about database internal implementations i mean the the sequel light documentation is actually really good explaining how the internals of a database work sort of traditional database uh for the internals of my research database i would read the paper it reminds you of scala i mean so keep in mind that the code that we wrote today you very rarely have to write yourself so my guess is you won't ever have to write pin unless you're implementing your own futures and most of the time these days you will just write uh async functions and async blocks which deal with all the pin stuff for you so you won't have to think about this uh that would be my guess it it should only matter if you have to implement your own futures how do you get error messages in vim is that ale yes that is ale ale is super handy for this it's ale plus uh rls although i actually mostly find that i don't i don't want warnings while typing uh i would rather switch to a separate terminal and get the errors and warnings there uh oh i'm glad you think so i mean the the topic we covered today is really challenging like arguably more so than the the stream on on how futures work because this is like very technical and nitty gritty and subtle and like theoretical detail um so if you followed along like i'm very happy it's hard too to to talk through it because so much of it is just uh i don't want to say ingrained but i've just i've done so many of these changes to pin now that it's it's almost like in the fingers and it's hard to put words to why things work the way they do so hopefully this made sense in the end um russ completions uh i'm using rls for that and language client neovim yeah do you recommend any specific repo to get exposed to a basic usage of pin besides tower um yeah one thing you can look at is uh there are a bunch of crates that give sort of handy combinators like smaller things so for example you could look at um some example of this like a while ago i ported um there's a crate called stream cancel that i wrote a while back i have a i have a branch now that implements standard future for it but you could try just doing that yourself in theory the the change there was pretty straightforward of moving to pin um there are other things that are sort of fun to think about like you could try porting buff stream to be async um but in general i would just look you could almost just go to crates i o and search for like async and look for smaller crates or crates that haven't been updated in a while they are likely to be on future 0.1 and they're likely small um and then you could just try to move those to to standard future and pin uh do you think the pin enables gooey engines like flutter to be implemented and rusted in an easier way um i don't think so so pin in and of itself only really provides a way to give you safe self-referential structs or types uh it doesn't really uh unless you need self-referential structs for things it won't really help you now that said it enables you to get async functions and async blocks and await and so in that sense it's really useful for ergonomics but that's more about async await than it is about pin even though pin is what enables it in the first place so it could only be that async await helps with flutter um but i don't think pin in and of itself does um i've enjoyed your videos but as a novice a lot has gone over my head yeah so these videos are very much targeted at people who uh are relatively experienced with the language uh and less so at like beginner content um and part of the reason for this is because i feel like there's a bunch of there's a bunch of material out there for beginners like reading the rust book for example is really good um i know there are some streamers who also do more like introductory rust programming content i don't have any off the top of my head um i know ryan levic did some uh i don't know that's his surname i don't know if that's how you pronounce it but uh he's done a bunch of streams a while ago and i i think hopefully he's starting up again um uh has done some stream that are a little bit more sort of introductory or beginner friendly so that might be something to check out um but if you just search for like rust live coding i know there are also more people who are excited to do it now i don't think i will be doing beginner videos anytime soon because i have enough advanced concepts to tackle um you might find for example the open source contribution stream better or the one where we write a hash map those might be easier to follow if you're in the earlier stages um and the in theory the next stream will be an open source contribution stream so that might also help uh lock free needed for signal slot communication for gui um i know that doesn't really relate to pin though uh will you still do go programming have you thought in doing videos on it so i haven't done go programming in a while now um i used it a lot many years ago but for the past like many years i've almost exclusively been using rust except for like coursework um i don't think i'll be doing videos on it i also just really enjoy writing rust code and i think it's fun and interesting like the fact that i can talk for three hours about one type in the language is really interesting uh some might argue that i should like go into formal verification and that kind of type stuff but that's too much for me uh how'd you get into rust was it just through your research i um i got into rust because when i started my current research project i had to pick a language for it and i was like well i couldn't use go but go annoys me a little and the system language from a zilla looks kind of cool so i might as well try and now five years later i have like 80 000 lines of code written in it uh much of it from like very old versions of rust um so yeah i mean i just sort of started and this is maybe a luxury of academia is that you can you can just sort of do that and pick pick a weird language and go for it and it's fine it's actually worked really well um i had a bunch of co-authors on this project too and and i think collectively we've found that rust while there's been there's certainly been a learning learning curve and some things feel more frustrating to do in rust than other languages um like you have to be more meticulous about your programming but i think it has probably saved us time overall and refactoring is a lot easier than i think it might be in other languages when will you tackle lifetimes the ultimate advanced rust topics so what about lifetimes you want covered because to me lifetimes aren't that complicated uh i should rephrase there's like higher kind of lifetimes which we don't have yet but and there's like the four constructs so there are some lifetime things that are somewhat complicated but in general i don't know what i would say about lifetimes for like three hours uh we we just finished for today but uh the topic for today was uh pin and unpin all right i think you know uh implicit lifetimes makes learning and understanding lifetimes hard at first maybe so i mean implicit lifetimes are they are a little annoying because it it hides you from things that will show up in errors later so that's possibly true uh oh yeah the tcp series was pretty fun it's a good combination of like real world but also a lot of rust oh i see it's true lifetimes have become like the uh a sort of canonical advanced rust topic even though like when you work with the language every day lifetimes are just not generally my issue like they don't come up that often really i think they come up more in when you start out with rust almost and then after a while it's sort of you just implicitly program such that they aren't that much of an issue maybe is async stood in combination with spin equivalent to normal stood um that's a good question so the question is basically if you take uh uh the like asynchronous constructs we have in rust and you just spin on them like you just keep calling poll until it returns is that the same as just using the standard library um the answer to that in the general case is no so for example uh imagine that you're trying to read from a tcp stream uh if you use async and you just spin on poll and then what's going to happen is you're going to be doing a read to the operating system so you're going to do a syscall uh the response is going to say not ready you're going to do another read it's going to say not ready you're going to do another read you're going to say not ready and so you're going to be spinning on the cpu and you're going to be spending a lot of resources crossing the the kernel boundary whereas if you do a blocking call like you do a read that's blocking the kernel is going to basically park your thread until the read is ready to continue or to proceed with some data uh which is far more efficient in terms of um because you're not spinning and you're not doing lots of system calls um now if you use a runtime then that changes things a little because the idea of a runtime is basically when you do the read and the operating system says not ready that future will be parked uh and the the runtime is going to go ahead and run other futures and where there's no more work to be done it's going to tell the operating system here are all the things i'm waiting on wake me up when any of them make progress and then the operating system is going to say you can now read on stream three and then it gets woken up and then it continues on the future that was passed previously um so when you have a runtime you get sort of the best of both worlds what's generally your daily issue with rust programming um i think my daily issues are a little bit different from many people's daily issues uh we have a codebase that's large enough now that there's some really subtle like business logic interactions that come back to bite me um i think the biggest issue i have that's like a general rust issue um is uh when i have so i'm using tokyo as my runtime and i have lots of futures running all over the place many of them are manual implementations um and this is an older codebase that used to be entirely synchronous that's been converted to be asynchronous uh and that comes with some issues where there are some pieces of code that are still blocking and you have to be really careful with blocking in asynchronous context because you're going to block the runtime uh and that can lead to weird hangs where suddenly your application is doing nothing it won't make any progress it's using no cycles but it's not finished um and figuring out like why futures are hanging is a pain so i spend too much time on that when will the next live stream be if you happen to know no pressure um that's a good question i don't know yet my parents are going to be in town next week um so probably not next weekend um i would say probably two to three weeks from now um it depends a little bit what it ends up being to if it ends up being an open source contribution stream which is what it's looking from the votes um then uh i'll sort of solicit some opinions on what people want to see me contribute to um and then we'll take it from there but my guess would be two to three weeks uh do you think it's worthwhile to learn Rust if you already know C in Haskell yes absolutely um i think C is a very handy language for doing like very low level work it's also a handy language to know just to know sort of how the computer works in some sense at this point there are very few things i would use C for um instead of Rust like i feel like i would just use Rust instead uh and Haskell so Haskell you can't write performance software in Haskell or it's very hard um and i kind of like the imperative style i don't want everything to be functional um so i mean i think there's absolutely space between C and Haskell for another language uh i think Rust is closer to replacing C than to replacing Haskell but then again i don't really know what software you would write in Haskell in first place like i've i've doubled a little bit in Haskell myself and i don't think i would use it for anything although i do use it for my window manager but that's separate um have you ever looked into the embedded working group stuff that's being done in the Rust ecosystem um a little bit um it's really cool i like that we're developing these uh these toolkits in Rust for building embedded devices there's been a lot of work on robotics for example which i think is really neat i think Rust fits well into the space too because it has um the same low level control as C does where you can actually work on embedded devices and have tight control over memory um but at the same time you get higher level constructs and and more safety guarantees which are necessary in embedded contexts um the biggest thing missing there i think is better support in libraries for operating without the standard library um basically a lot of crates rely on uh stuff from the standard library from stud which makes it hard to write things in embedded because you can't rely on any libraries or crates that require stud and so i think having a push in the ecosystem to move people towards uh no stud or in particular to move towards only requiring alloc for example um so only requiring the allocator and all this is called interface for crates that can do that would be really good uh i'm the person that's been testing your pr to mysql async oh yeah that's been really cool so what they're talking about here is the mysql async crate is future 0.1 and i i i did an effort to port it to standard future and that now basically works you can write like async and await around mysql connections which is really cool um and it's sort of like the mysql async has sort of been uh the mysql library has been the primary like focus for a long time and the mysql async has sort of been a little bit of a not an afterthought but it hasn't received as much love um i think now with async await really coming though it's going to be the next uh it's going to become a lot more important um are you interested in x-mode ad like we are in rust yes i mean i already use x-mode ad i would be happy to switch one to one that uses rust because i currently can't really understand all of my own config which is a little weird um i don't really like having to recompile my window manager to change the config though so i don't know exactly how those would interplay i would really be interested in a general debugging video like with gdb or your perspective on it and maybe more particularly on a future context yeah so debugging and rust you can do with gdb um there's a lot of stuff out there there's actually um i did a mit lecture series a while back with some people from my lab where we covered a lot of just useful topics to know in computer science and one of them was a video on debugging and so you might want to take a look at that that covers gdb in some detail and applies the same to rust code mostly um doing some performance profiling might be fun although there i would look at the um the stream where we port flame graph to rust that has some of that in it um but debugging in future context i think is a big topic where i don't have the answers yet either i think is an ecosystem we haven't figured out exactly how to do it the tracing crate is really promising here um so i've been following that development a little bit and because it's going to be integrated in or tokyo is going to be using tracing as well that's going to make it a lot easier to do uh tracing in future context and see which futures are stuck where um so tracing is a is a general logging like crate um but that has a lot better support for things like keeping track of the context where the log statement happens uh and that's going to help a lot but i don't know exactly how far along that is there's a work on something called the the tokyo console which is basically like a way to get insight into what the runtime is doing which i think is going to help here too um but i think we we haven't really seen how that whole part of the ecosystem is going to shake out yet um was rust the first language ecosystem you got this deep into and you think mastering a language ecosystem offers a similar growth opportunity to learning a new paradigm um so i think so i got pretty involved in the php ecosystem back in the day i wrote a bunch of like libraries that got used for things um i got involved in the go ecosystem in that i built a bunch of libraries there too i just really like building libraries um and i also tried to contribute to the language but both in php and in go that was pretty hard at the time and i think to some extent still is like go is not a community language uh and so it's hard to be involved whereas with rust uh i also started writing libraries um but then i also got more involved with the community in part because community or the language is more community oriented um and so i actually felt like i could contribute to the language as well uh and so that was good uh so so in that sense yes it's the first one i got this deep into but it's not the first time i get into ecosystems um and do i think mastering a language ecosystem offers a similar growth opportunity to learning a new paradigm i don't know how to parse that sentence but i'm gonna go with yes i think getting involved with an ecosystem is uh good for you as a programmer uh that's what i have to say about that uh did you take a look at the bastion project i don't think i know anything about bastion uh where's the lecture series uh the lecture series are called hacker tools um and it's at hacker-tools.github.io oh sorry this is light sorry people at home and it has uh videos on a like short lecture videos and notes about a bunch of topics and you'll see there's one on uh where is it program introspection it's on like debugging and profiling so i would watch that video are there any problems or tasks for which tokyo runtime isn't a good fit and why um so the tokyo runtime is multi-threaded um and so there are some workloads where you really want single-threaded operation tokyo has support for it but it's less of a focus um but i don't really know where that would matter i think in embedded environments you might not want to use tokyo because it relies on a lot of um basically os integration um so in embedded context you might want to write your own executors for example um apart from that i've been pretty happy with tokyo i mean it has the advantage that it's being used in production in a lot of places so it has a lot of maturity um and so for the database like the research database i'm building we've found it to be like very performant which is helpful uh and i know that there are many people who use it who care a lot about performance and so the it is very focused on like being fast for the for the cases that matter in some sense like empirically have turned out to matter uh i also know that they're pretty devoted to backwards compatibility which is useful because it means you don't have to like break your code all the time although the move to a standard future is pretty breaking i don't think i missed any all right i think with that we're gonna call it a day um if you have thoughts about the episode like feel free to leave comments i'm gonna upload the video shortly after this um or if you have like follow-ups i've had a bunch of streams now where people have posted really helpful comments and follow-ups on problems we ran into during the stream where they find solutions references to explanations of particular concepts find any of those let me know um if you have ideas for upcoming um upcoming streams that you want to see just like ping me on twitter send me an email um and i'll try to add them to the voting site or if they're really great i might just do them uh and if you want to hear about upcoming streams just like join the just follow me on twitter or i'm also on mastodon um and that's where i announce all upcoming streams they will all be on both youtube and twitch just like this one uh and if you want to support the amazon wishlist is there um how does one get begin to get into all of this uh you start from the beginning you read the rust book it's very concrete and practical uh thanks everyone uh it's been great and i will see you next time so long for well i'll be there saying goodbye