Overview

About this video

What You'll Learn

  1. Explore how Rust futures stay lazy until an executor polls them
  2. Use Tokio spawn and join to run concurrent async tasks together
  3. Build a custom future, waker, and executor that reschedules delayed work

Senyo Simpson joins David to dig into Rust's async/await. They cover lazy futures, the Tokio executor, spawn and join, then build a custom Future, executor, and Waker from scratch to show what happens under the hood.

Chapters

Jump to a chapter

  1. 0:00 Viewer Comments
  2. 0:55 Introductions
  3. 0:58 Introduction and Welcome
  4. 1:38 Guest Introduction (Samuel)
  5. 2:09 Samuel's Background and Interest in Rust
  6. 5:45 What is Asynchronous Programming?
  7. 10:56 Async/Await in Rust vs. Go
  8. 12:20 Starting the Code Demonstration
  9. 12:50 Spawning & Joining Futures
  10. 13:30 Setting up the Tokyo Executor
  11. 15:55 Understanding Lazy Futures & Await
  12. 20:03 Demo: Synchronous vs. Asynchronous HTTP Requests
  13. 28:01 Using `tokio::spawn` for Concurrency
  14. 30:19 The Need for Waiting: `tokio::join!`
  15. 32:31 Recap of Async Basics & Tokyo
  16. 33:43 Discussing Sharing Data (Send/Sync/Pin)
  17. 35:42 Lazy Execution, Executors, and Wakers (Q&A)
  18. 37:59 Understanding Futures Internally
  19. 41:17 Implementing a Custom Future (Poll::Pending)
  20. 41:20 Writing Our Own Futures
  21. 44:49 The Role of Wakers
  22. 47:18 Implementing a Custom Future (Poll::Ready)
  23. 49:50 Writing Our Own Executor
  24. 51:46 Executor Structure: Task Queue & Channels
  25. 56:30 Executor `run` Method
  26. 1:00:14 Implementing the Task Struct
  27. 1:02:04 Pin, Box, Mutex: Why are they needed?
  28. 1:05:28 Implementing ArcWake for Rescheduling
  29. 1:11:35 Creating a Future with Actual Delay (FutureDelay)
  30. 1:17:39 Running the Custom Executor with a Delay Future
  31. 1:22:17 Conclusion and Wrap-up
Transcript

Full transcript

Generated from the English captions. Timestamps jump the player to that moment.

Read the full transcript

0:58 Introduction and Welcome

0:58 And so, and welcome to today's episode of Rawkode Live at Rawkode Academy. Today, we are doing a hands on session looking at how Rust and asynchronous programming work together. Before we do that, there's a little bit of housekeeping. Please like, comment, share, and subscribe all the things on YouTube. Can always find this channel available at Rawkode.live. Also, we have membership options available and courses where you can come and learn the complete guide to InfluxDB. We'll check out the different options available there, and feel free to jump into the Discord. We have around 500 people in there now

1:34 talking all things cloud native, Kubernetes, and everything in between. So come and say hello, and I look forward to meeting you. Okay. So because I am not a Rust person well, I'm trying to be, but my synchronous Rust is pretty poor as Samuel is gonna find out. You have a wonderful guy today. So, hey, Samuel. How are you doing? Good. Thanks. And yourself? Yeah. Yeah. I'm doing alright today. Awesome. It's always fun to be able to write a little bit of Rust for people, so it's gonna be awesome. Yeah. I'm I'm sure it's gonna be great. I'm excited. Awesome.

1:38 Guest Introduction (Samuel)

2:08 Can you tell us a little bit about you and introduce it off to the audience, please? Yeah. Sure. So, yeah, I'm Samuel, as as Dave's mentioned. Cool. So, yeah, I work as a platform engineer at a company called Aerobotics. It's a company in Cape Town, South Africa where I live. And, yeah, we build software for the agriculture industry using a whole lot of nice buzzwords of drones, AI, machine learning, blah blah blah. Really cool stuff. I actually started off as a data scientist, surprisingly, and then about, like, a year and a bit in, figured I enjoyed engineering more,

2:09 Samuel's Background and Interest in Rust

2:51 and so moved over. And then, yeah, I've been, like, doing Rust, like, in my free time for, like, a year, roughly. So, yeah, it's pretty much how I ended up here. I'm assuming being originally a data scientist, you must you must be a Python developer as well. Right? Yeah. Yeah. I know. I know a lot of Python. So what caught your attention about about Rust? So at the time so this was last year. So, yeah, last year, was basically learning a bunch of programming languages. So last year was, like, my first year as a full

3:26 time developer, and I was just learning languages because everyone's like, learning new languages makes you a better programmer. So I started off with Haskell. That went terribly. Wow. Jump in on the deep end. Yeah. I know. It wasn't it wasn't a great it wasn't a great time. Honestly, couldn't really figure out left from right there. And so then moved on to I tried closure. That was that was a great experience. Did a little bit of go, and then then at some point, I was like, let me learn Rust. And that yeah. I read the book.

4:00 I know yeah. I mean, just from there, it just caught my attention as, like, a really interesting language, and then also solved a lot of problems that, like, I had seen developers make in in Python, myself included. So for instance, like, a function just returning none or some random value or whatever, and then it blows up in your face at runtime or whatever. So there was situations like that, and I was like, you know, Rust has these nice guarantees, and, like, kinda, like, you know, forces you to program it in a in a bit of a better way in my opinion. So

4:34 that that kind of, like, just stuck with me. And then I've stumbled upon awesome Rust mentors, And then from there, found a bunch of people in the Rust community, and they just stuck. Like, that was that was it for me. So, yeah, since then, pretty much been doing Rust like in my personal time as much as possible. Yeah. I think the the Rust community is it should be like an example of how to make a welcoming programming community. Just always taken aback by how happy and charitable everyone is with their time and their knowledge. Like,

5:09 just it doesn't matter what your problem is or what you run up against. There just seems to be always someone they're willing to help. Hundred percent. Yeah. It's it's really amazing. And I think it's it's probably one of the biggest contributors to our Rust is doing so well as a language. Just because it's a difficult language to learn and so having a a community that's there to support you through it actually makes a lot of difference. Yeah. Yeah. I've I've been writing Rust for a little while now and still bang my head against the table quite often. So Yeah. No.

5:36 It is definitely difficult to learn. But I am writing much more functionally correct code because of, the type system and the guarantees and the and all that. So Awesome. Sure. Alright. Can you then do us a favor and maybe just tell us a little bit about what we mean by asynchronous programming? Sure. So yeah. So just for context, like, I am I started learning about, like, async, I think, late last year. Well, I mean, I started learning about Rust late last year, but it's been a it's been an interesting journey. And so the the I guess, most exciting thing about

5:45 What is Asynchronous Programming?

6:12 async for me was that I could actually learn, like, the details of it, and so kind of, like, how I got here. So with async programming, like, the main idea is to solve, let's say, a class of problems that allow your code or applications to be more scalable. And so you basically wanna run like concurrent software. So you wanna handle multiple tasks all at one time. Mhmm. And this is good for things like web service. So I I guess that's like the canonical use case where, you know, you have like multiple in flight requests and you wanna be able to handle

6:52 them all. Obviously, if somewhat taking extremely long or your concurrency model isn't that great, you're gonna have, like, request backlog and then just start getting 500. Yeah, async async programming programming kinda, like, came in to fit that void as far as I understand. And so the the main use case or way it fits in the basis with IO bound tasks. So that's with most of the time taken for their, like, execution is or what's dominated by waiting. So if you can think about it like making a a database call, for instance, you do a bunch of work, make a

7:35 database call, wait until it it resolves, until you get in your information back, and then you can continue working. So there's, a big span of time that's spent waiting. And so, effectively, what you wanna do is kind of, like, maximize the amount of time that the CPU is being utilized. So you don't wanna keep wait you don't wanna have this dead time where a request comes in and then you've gotta wait, and then nothing happens between when you make that request to the database and when you get the information back. And so async programming kind of fits in

8:06 that way by allowing tasks to basically, like, give up the or, like, yield the execution to the CPU at points in time and say, like, I've made a request. I'm gonna be busy waiting. So something else can run-in the meantime. And then when that thing finishes right or, like, when I'm ready to run again, I'll let you know, and then I'll proceed to run. And so that it's just kind of, like, unlocked a lot of performance. And so yeah. I mean, yeah, that's pretty much pretty much it. Long long explanation. No. That that works really well. You know,

8:44 like, I think when I when I first started working, like, in code professionally, and I won't give you a year because that'll show my age, but, you know, I was working with with parallel and PHP languages, which doesn't actually have, like, any native concurrency or asynchronous primitives And then Mhmm. Blocking the the request. Well, like, you know, request comes in and then blocking, so you make some other external questions in the back. Yeah. It's just not gonna ever scale. Right? Like, concurrency and asynchronous. I understand. Yeah. I've had, like, a quite a bit of experience with Tango's kind of, like, sync

9:17 model. So it's kind of like a basically, you can think about, a thread per request. And so what happens if, like, if any part of your of your request is really slow, then all of a sudden, get this, like, huge backlog of requests coming Exactly. Coming back because something might take, like I don't know. Let's say, like, in a bad, bad example for instance, like, if something took two seconds and your requests are coming in at, like, every millisecond or something, yeah, because, obviously, things are gonna go wrong. Right? So so, yeah, async really fits that

9:48 that specific, like, problem space and, yeah, basically solves for that. Yeah. I think, you know, maybe a a lot of the people that subscribe to the channel may be familiar with Go, which has a relatively hands off asynchronous model and that you never really need to worry about it. You just say, go run this thing in the background. Yeah. Rust doesn't quite like that. Right? It's a bit more complicated. Yeah. Yeah. I think I actually I actually think that goes way of doing it. Like, okay. I don't have very informed opinions here, but I do think that

10:20 it's actually quite a nice way of handling it in the sense that because it effectively does the same thing, but just behind the scenes. So you as a as a dev don't have to worry about actually writing, like, async await. I don't know actually which one's better or, like, the economics of either. But, yeah, in Rust I think in Rust, I think Nodes, JS, Python, and a few others, yeah, it's really explicit. So you'd have to write, like, you know, your your code with async await and have your red or colored functions as as that essay

10:55 says. The Rust model seems to be really similar to the node one. At least I see a lot of similarities and even just the syntax and some of the like, for me the problem parts is always like you have to defend your function that says sync and then it has to be called from an asynchronous context. You can't just go through it around a waste anywhere, which in the Go world is unheard of because in Go you'd literally just put Go before a phone call and that's it. Right? It just it just does it. But with with Node and with Rust,

10:56 Async/Await in Rust vs. Go

11:23 the similarities are there and I have to actually think up front about where I need the concurrency to happen or Yes. Whatever. And then I think my first time trying to write is synchronous Rust and realizing that I had to have the executor context on the main function that then delegated the access and blah blah blah. I was just like, ah, it's like Yeah. Definitely. It is it is definitely difficult. Maybe a little bit overwhelming. I guess for me, like, that's that was my introduction to async programming in the first place, so I kind of missed it.

11:53 You're you're gonna make it easier for all of us today. Right? Hopefully. But yeah. Yeah. I I really enjoy, like, so far as as much as I've as played around with asynchronous stuff. I've enjoyed it so far, but I have heard there are some pain points, I think, for a lot of people that are using it in production. So but it's also still new. So we'll get there. Alright. Well, how do you wanna kick this off? Do wanna will we move over to the screen share and start writing some code? Yeah. Let's do that. Let's definitely

12:20 Starting the Code Demonstration

12:27 I think that'll I think that'll be fun. Alright. We can see Versus code, but you may have to pump the font size up a little bit. Okay. Cool. Cool. Yeah. Let me do that. I think you can just command plus a few times or change it earlier. That would work too. Yeah. Let's see. Go 20 go 24. That's what I use. Okay. Cool. Yeah. Now we're kicking. Like it. Awesome. Awesome. I bet that's massive on your side, though. Right? Yeah. I know. Really is vegan and Spanish because I use, like, 10 or something ridiculous like that as my normal

12:50 Spawning & Joining Futures

13:15 font. So 24 is is huge. But it's actually it's actually a great quality of life. We've got a hello from Javier in the chat. Hey. What's up, Javier? Yeah. I know. Cool. So, yeah, I was thinking, like, it would be cool to kind of, like, run through just a bit of the kind of, like, basics of async and just kind of, like, walk through some very beginner stuff and kind of, like, break some sort of, like, assumptions that we might have of it or whatever. So Perfect. Cool. So, firstly, yeah, one thing I think that's become, like,

13:30 Setting up the Tokyo Executor

13:55 a big deal in async is, like, the executor. So, you know, we've got like Tokyo and async standard, and then they're not entirely compatible and whatever. So you basically have to choose your one upfront and, like, ecosystems are built around each one. For for this use case, I'm gonna use Tokyo purely because I know some stuff about Tokyo and don't know too much about ASX standards. So it's nothing nothing against the ASX standard piece. But, yeah, I mean, it's just basically all I know. So yeah. Basically, just like a quick just running through, like, a quick

14:34 creating a quick asynchronous function. So Tokyo is the executor, and then it's it's got, like, an entry point. So in this case, this async fun main. And yeah. I mean, this is basically all we need to do to make code asynchronous so we can actually enable, so increase the font of my let's see. I think the size of the sidebar is okay if that's what you're worried about. Oh, the terminal. The terminal. Let's see. Oh, there we go. Yeah. Perfect. Sweet. Cool. Yeah. So this is basically all you need to do to make a asynchronous

15:23 or make your code asynchronous in the beginning. For the purpose of this one, I'm I prefer to use so you can use a multi threaded or single threaded async executor. What's going on now? What did I do? Yeah. Oh, sorry. I think that might work. Ah, yeah. There you go. Currently. So, yeah, you can have a multi threaded or single threaded one. For the purposes of this bunch of recommendation, she's a current rate, which is just a single thread executor. I think it's a bit easier to understand the model. So I actually have this image

15:55 Understanding Lazy Futures & Await

16:16 here that I was so I was actually writing a blog post at some point that I never got to finishing. But you can imagine that, like, let's say we have task a and b here, and it's executing. So this part portion is where it's doing some work. This part where it's empty is it's waiting. Similar for task b here. All of all both of these tasks will run on, the same thread. So task a will run. They will just start it with task a. Then when it gets to this yield point, it'll give up control. So

16:49 in async, tasks are cooperatively scheduled, which just means that they themselves told the execute like, the executor when they're when they're, like, blocked, and then they'll give up the the CPU, basically. And so it works on, like so you have to have, like, basically, like, good access. So in in this case, what'll happen is if you had, a CPU bound task and it never yielded the CPU, then nothing else would be be able to run at the wall just be blocked in essence. But, yeah, we have this, like, task a, which runs, and then we have

17:27 then it yields the CPU, and then task b will start running, but it's all on the same thread. This is a bit easier to understand than having it on multiple threads. It does the same thing, but, like, on multiple threads, basically. Cool. So, yeah, this is basically all we need to have an async function. And if we can't go around this, Actually, it will compile and work. Yeah. I've heard the Rust compiler is really fast. Said nobody ever. It's it's working literally. Also, on a MacBook, it's arguably also not as fast as of others. Cool. So,

18:08 yeah, we've we've run this function. We've got a hello world out. Feels very synchronous, so nothing too crazy. And effectively, it's well, it's not, but effectively, it's run a synchronous in a synchronous fashion. And just to just to kind of, like, unpack this, so this Tokyo main flavor current thread is effectively the same as this. So it's obviously a macro, and it does the same thing as let's see. I can comment this out. So these two things are equivalent, barring that there's no comment. And so, basically, what happens is it creates a new runtime, and then it has this block

18:59 on method. And what this does is, as it says here, it's it'll run the feature and then block until it's complete. And so, basically, this this here is here is your feature, and you have this read feature. So features are often made up of other features. And so in the case of, like, let's say, a web server or whatever, you'll have, like, this outmost feature and then all of your tasks running. Okay. Yeah. It's running. All of your tasks running within that feature. And then only when this root feature, so the, like, top level feature results will this

19:36 finish. And so, yeah, basically, we have this async function here, which does the same thing. So this should effectively give us the same outputs. Let's see. Yeah. Hello. So we've we've we've come full circle and pretty much got the same thing. But yeah. Now we can try and do some more sort of, like, useful stuff. For instance, so let's say we wanted to so let's say you wanted to just make, a request using request so we can use a the request is async, basically, it uses Tokyo underneath. And so let's say we so we can create a client.

20:03 Demo: Synchronous vs. Asynchronous HTTP Requests

20:37 The request is a h HTTP client? Yeah. Yeah. It's a HTTP client. So you can get a new client. Think this is will work. Let's say, get. What is this? You're all gonna think. Rustling. Okay. Cool. So this dot send is a this return this actually returns a feature, this dot send. And and then we've gotta await that feature to basically run it. So one thing about Rust's async model is that features don't run until they're pulled, which is basically just to say that they do nothing. They're, like, late they're lazy by nature. So they do nothing until

21:43 you kinda, like, force them to run or or drive them to run, which is what this await does. So if you don't use start awaits, nothing will happen. So if you create a future and never create you the result, then that future will never run? So, yeah, if you if you never await a future, it'll it'll basically never run. Okay. So we can we can actually, like, test that out. So firstly, let let's maybe just, like, print a let's just do something simple. So in this case, the same thing. So we'd expect this to return, like, a

22:24 200. It'll just be, like, response time. I should definitely get a fast computer. It's not good. It's the compiler. Cool. So we got this two hundred hours. But let's say we didn't run this. So instead, we just had send here and then did nothing with that with this. So now you can actually see from the from Rust Analyzer that this output is a future with some response in error. And, okay, now we're getting an error here. So this nothing will happen here, basically, but there's nothing that's gonna get you can't actually print a future. So Can

23:14 we can I make a suggestion? Could we, like, create a a function that just touches an empty file on the disk and then schedule that and then not await it and the file wouldn't be created? But if we did await it, the file would be created? We probably we can do that. We can do that. Then we figure out how to do that. I guess even just a even a print line statement would work. Like, if we wouldn't see the print hello if the, you know, if the future of the asynchronous function was to print

23:44 hello, we shouldn't see any output, I guess. It would be the same effect. Sorry. What what do you think? I missed that. Yeah. We you I was just saying your actual your idea is better. Like, if we just do a print inside of the future like, if we create a new function called async, print hello. Like, it should never print hello if we don't know where to Yeah. Yeah. Rather than trying to remember how to do the fail system calls because I definitely don't remember. Yeah. So let's say, yeah, print hello. So takes nothing, and then let's just close

24:19 the Rawkode. Cool. So in this case, we could actually just say oh, so let's say, like, we'll get feature equal So in this case, like, we would expect so pretty much this Hello Rawkode wouldn't run about this print line would. Yeah. Yeah. So if we do that, then that's pretty much what should happen. Okay. So now it's telling us that we didn't use That is just a warning. That can't So you can see, like, you know, this hello gets printed, but then if you go, like, future dot await, then you should see hello Rawkode and then

25:22 and then the hello. Right? Yeah. So that's pretty much what we've gotten by awaiting this feature. An interesting thing, though, is that this technically is still just running synchronously. So even though we've got this, like, async and Tokyo is running, there's not multiple tasks being run at the same time. So we can can do that just using let's say, we can make some multiple requests. And there are some interesting things here. My account, so if we have And then if that same request again, so try and go. Just give me a second. Yeah, there's quite a few interesting things that

26:23 could happen here. So if you've got this and we kind of, like, duplicate this again, so if we have So you're gonna make two HTTP requests? Two requests. Yeah. So if we do that, then okay. Let's call this, like, results or response one, and then this response two. What will happen is that still in this setup Okay. It's the right number one. This will still be synchronous? Yeah. It'll still it'll still basically be synchronous. We'll have and then. It's live taping. Yeah. Cool. So in this case, it's basically actually still synchronous here because parts aren't really being spawned

27:38 to run asynchronously. So in this case, it'll still it'll still run. So even though this this result one will be like an IO task, so it has to wait for a response. It'll just still run-in the order that it's that it's there. And so we can actually get around this. Alright. So still still in order. And if you run it multiple times, it'll continue to be in order. And so get it to actually start using async properly, I guess, then we can start spawning tasks. So Tokyo has this thing called spawn, which I think it's Tokyo.

28:01 Using `tokio::spawn` for Concurrency

28:23 Yeah. And this spawn takes, an async function, basically. I copy this right now. Cool. Alright. Yeah. And so this will spawn another task onto the the executor, these will run-in asynchronously, basically. But we might also find some weird behavior coming out of here, which could be quite interesting. So let's say So we have that. Then what we want here. Time. Cool. So so what did I do now? Alright. It doesn't matter. Cool. So now we have these two tasks basically being spun up. So this spawn will spawn an asynchronous task onto onto Tokyo, and this will do. So these

29:39 weren't these weren't blocked. So what we'd expect here is for so because these get scheduled asynchronously, you would expect this to run this print line run async first and then whichever order is complete. It might still complete in the same order it's been written purely because, yeah, the one request happened earlier than the other. But sometimes it doesn't. I had fun yesterday figuring that out. So if you run that, then cool. So yeah. So this is actually an interesting an interesting outcome here. And so this has to actually do with the fact that, like, there's

30:19 The Need for Waiting: `tokio::join!`

30:29 when I was talking about having root features and features or a root task and then task being spawned within sort of, like, this root task or being a combination of other features, what's technically happened here is that that block on function that I had earlier or that I showed earlier, it only waits for the top level feature to resolve. And so what's happened is that these two tasks have spun off, but then the top level feature is basically like this in essence. Yeah. And so when when it got here, then it's finished. The top level feature is

31:07 finished, and so it just doesn't complete the rest. So a quick way of actually handling this is to is to use Tokyo joint. And this basically just says it waits for each task to finish before completing, basically. So it'll wait for a lead. And spawn returns, like, these things called join handle. Yeah. I think I've had this problem before when I've been doing asynchronous. I'm like, why is it not running? Yeah. It's a bit of it's a bit of interesting one. So, yeah, in this case, now we've got this, like, Tokyo joint, and then this will basically wait

31:50 for these two to finish. So I don't know which order they'll finish in. It say it can change. It could be in the same order. But now we'd expect to see run async and then the status of these two things being printed. Cool. There you go. Oh, two first. Oh, two first. Cool. So so yeah. So now you can see that now this is actually all running asynchronously, yeah. I mean, which is the call and and exactly what we wanted out of that system, basically. Okay. Can I just summarize this so that I make sure I

32:31 Recap of Async Basics & Tokyo

32:33 understand this all correctly? A %. Okay. So can you scroll up one line? There we go. Okay. So we have this Rust macro, which is the Tokyo main, and where you're setting it to use an executor which is current or a single threaded. That macro is doing the boilerplate that you pasted earlier which sets up a Tokyo executor blocks on an asynchronous function which is an essence just our main function. Yes. Now here you have two different requests using two different clients to the Rustlang website, which are being blocked on using the await syntax. Those are both spawned asynchronously

33:17 using Tokyo spawn construct, which just a asynchronous function and runs it in the background and returns you a handle to that asynchronous task. Mhmm. And then at the bottom, you are using a Tokyo macro again, allows us to just wait on all of those pending tasks that we have by passing in the handle and letting it do its thing. Yeah. Pretty much. Okay. I got one question then. Hello? Is the standard procedure to then let me see if this is right. Do we need the two clients, or could we create the client on a higher level

33:43 Discussing Sharing Data (Send/Sync/Pin)

33:51 than a function and pass it into the asynchronous functions? Or is it would you just try and isolate that as much as possible and have multiple clients? So it's it's a bit of a difficult one, mainly because so it's not, like, extremely concrete in my mind yet. But in so in this case, when you spawn something like the the the task normally takes ownership of those of those variables in that case. So in this case, client, we wanted to be owned by a certain task and and all these tasks. And I and I'm not entirely sure

34:31 why, though, I mean, like, just a a broad assumption there's you want a task to own, like, its entire or, like, all of the variables it uses because task can also get, like, shipped across threads, for instance. So you it might let's say, in a multithreaded context, it might start off on thread one, but then end up on, like, thread three, for instance. Mhmm. And so yeah. I'm I'm, like, pretty sure that's probably a big reason why it needs to needs to own its variables. And so I'm not sure about kind of, like, passing in the same client

35:11 into multiple tasks. I'm pretty sure it could probably be possible, but I I don't have enough knowledge there. I'll I'll I'll take that. Also, I'm curious about the join handle. Is is that a feature itself? Can we await the join handle if we want to do it? I actually don't think so. I'm not sure. I'm not sure. I don't I don't think it's I don't it's actually no. It doesn't look like it. Okay. Doesn't look like it's Alright. We have a a question in the chat as well from Russell who's asking, is the lazy execution mechanism the

35:42 Lazy Execution, Executors, and Wakers (Q&A)

35:45 same depending on the executor behavior? For example, if you choose multi threads rather than a single thread, does an async function only get called when awaited as well? Yeah. So that that kind of, like, model with the with futures or tasks only being run when they're awaited, that's just like a Rust construct. How that's handled sort of like by your executors is just like an implementation detail. So, yeah, it's basically it's basically just like how the languages are solved for that. And I think the the reason why it's built like that is because for for them to be zero cost.

36:30 And so what's happened, at least from what I've read, is early on, Rust actually had its own, like, sort of, like, green threading library. And so but that had, like, global costs. So, basically, it would put put a performance cost on in using Rust whether you use those that set of functionality or not. And so with having lazy features, it it basically means that, like, no work actually happens before, and they're and they're compiled down to state machine. And so no work happens before, so you can use them and there's or you can, like, create them and use them freely. And

37:11 and if you don't await them or have an executor, then you pay no performance benefits. Obviously, then they're useless, but that's the that's that's the premise. So I think I think it's gotta do more with the with the kind of, like, design constraints that Rust Lang imposes by having that zero cost guarantee. And so that's why it's it's set up in this way. But nothing, like, happens automatically. Whereas, like, Python or whatever, once you start the event loop, it just does its thing. You know? And same with, like, node and others. Alright. Perfect. Cool. Sweet. And so yeah. So that's pretty much

37:57 on some Tokyo stuff. I was also thinking it would be cool to kind of, like, run through a bunch of stuff around futures themselves just because I pretty much find that interesting and just talking around, like, how they're created, what they do, all of that jazz. Yeah. Let's do that. So k. Sweet. So just give me a sec. Sorry. Cool. Yeah. So, obviously, we've been talking a lot about features and tasks and whatever. And so, yeah, basically, like a feature represents or it's kind of like canonical definition is it represents some work that'll be competed in the future. So it's

37:59 Understanding Futures Internally

39:05 like some computation that'll be competed in future. Rust is a little bit different purely because that feature, like, it has to be pulled kind of like manually. And so you can see that, let's say and so yeah. Then we have these like async keywords. And so basically, async is just a way to denote a function basic, but it basically just returns the future. So if I do like this, like, this future is some async and just some number, you can see that this return is something that implements future, and its output is is an integer.

39:50 If you make it something else like unit type, you get this, like, unit type outputs. Let me just use this one. Yeah. And if you do, a string, you'll get an string. So bay and so yeah. A feature in Rust is basically something that's oh, what am I saying? That is is basically something that implements the future traits. And so yeah. In this case, in this case, like, we've got this feature that basically just returns 16 here. Yep. Can people that are familiar with the node ecosystem just replace future with promise in their head and it's like the same thing?

40:35 I think so. It kinda looks that way, so I think I'll I'll just try and do that. I think so. I'm not entirely sure. I don't I don't have too much operating experience with JS or Node JS. Lucky you. Yeah. I avoided that one. Cool. Yeah. So, I mean, we have this feature again, like, you know, we can go and do some work here, and this obviously won't do anything until we await it. Oh, sorry. Cool. So yeah, and so we have this feature. And so, like, a big question I had, like, initially when I

41:17 Implementing a Custom Future (Poll::Pending)

41:19 started is, like, what exactly is going on with the future and, like, how does it run? So we can actually do some cool stuff here. So let's I just need to just import a bunch of stuff. So let me actually just copy and paste this because this just be way easier. Cool. So let's say you wanted to make so we can make our own feature. It's actually not that difficult to make a very boring feature, so we're gonna just do that. So let's say we have this ending future. Are you gonna tell me that futures are

41:20 Writing Our Own Futures

41:59 just a tree implementation? Technically, yeah. Everything in Rust is always a tree implementation. Trades are trades are beautiful. They're really they're really great. So there's no there's no wonder. Cool. So we can create a oh, what did I do? Yeah. Yeah. We can create a trade by implementing future for what this thing that we've called pending future. Cool. And this is basically the the structure of that of of the future traits. And so we have to have this top output. This is like an associated top, so we're just gonna return basically nothing. So it's just an implementation

42:44 of a pull function that checks to see if the work is completed and then returns it? Yeah. Pretty much. Pretty much. Nice. And so okay. Cool. So what'll happen is that when when you pull, it'll return this kind of like pull object here. And this can either be pull pending or pull ready. Those are the two, like, variants. So pending already. And so we're gonna do something, like, really interesting here where we're just gonna return pull pending and, like, prints. I've been pulled. Mhmm. So yeah. And so what'll happen is, like, basically, when the executor

43:25 runs in your way, the task, it'll basically start pulling your task for you. And then there's, like, a whole lot of details there. So we can make a pending future here. That's the other ring. And the wait. But the interesting thing here is that, basically, we'll see when it runs, whenever that is. Cool. So we've got this, like, I've been pulled, and then now our executors actually hang. So, like, there's nothing nothing's happening. And the reason why is because, as I said earlier, like, we've got this, like, outermost feature, and that feature what's the name of it for? Like

44:18 oh, it's blocked on that outermost feature. And so this, like, feed and so this one that we've created internally, it has it it never completes. So by having this poll pending, it's basically never completes. And so the outermost feature won't also complete, and therefore, it just hangs, and so nothing happens. So we can also And kinda curious, like Yeah. It only pulled once. Like, I would expect it to maybe to pull more than once. Is that something that's then controlled by the implementation? Yeah. So that that's actually an interesting thing and also like a great

44:49 The Role of Wakers

44:54 or just like a thing that Rust did. So we can actually we can actually we can actually fix that. So let's say, I saw this trick the other day. It's actually cool. So then I can explain it later. Okay. Cool. So if you do this if I think this should work. So in this case, we should see a lot of output here, hopefully. Yeah. You can see There we go. So quick. Yeah. Right? Stop. Kiggle. That's kind of what I I actually expected that behavior by default. So maybe you can Yeah. Walk me through that one. Yeah. Cool.

45:36 So that's basically like the the way kind of like Rust has set up the system. So the idea is that when a task gets pulled and it doesn't complete, you don't wanna, like, keep pulling it all the time. So you don't wanna have you don't want it to be like this busy dude where it's like, hey. Have you finished? Hey. Have you finished? Hey. Have you finished? And then it keeps saying like, no. I haven't finished. Right? So what it does is that it'll set up so we have these things called wakers, and wakers are basically a way to tell

46:09 the executor that, like, hey. The thing that I've been waiting for is now complete. I'm ready to be scheduled, so let me run effectively. And so what'll happen is that your task will run and then it'll, like, yield or say, you know, it's pending. Then when it's ready to run again, it will call this, like, wake object or it'll get this wake object and call wake, and that'll reschedule you back onto the executor. So in this case, with Tokyo, for instance, because we didn't reschedule it, nothing happens, basically. It just it just, like, we've called it once, and then it never

46:49 tells Tokyo again that, like, I'm I'm ready to be pulled again. And so nothing happens until that happen, like, until you you wake your task, basically, or your future. Okay. Got it. Thanks. That makes sense. It does. Cool. Cool. So so yeah. So that that basically that's and where was that? Yeah. Cool. So oh, okay. Cool. So now we can also have, this we can just return ready as well. And so this will do what you would expect it to. Like, it'll print out this. I've been pulled and then print hello. So cool. Yeah. So so now we've got this

47:18 Implementing a Custom Future (Poll::Ready)

47:35 feature that basically does exactly what we wanted to. And it's actually not that complicated as as we've seen here. Like, we've been able to create one, you know, without without too much ceremony. Obviously, real shooters are way more complicated. And so yeah. And so that's pretty much like the that's on on on having or creating your own features and then wake us. Cool. I don't know if you have any more questions. Yeah. I I guess so. Like, you know, people wouldn't really be expected to implement a few shares on their own. In fact, it'd probably be discouraged. Right? Just use

48:17 Yeah. Just use what is already provided. Yeah. Pretty much. Okay. Yeah. And and that they I mean, there might be there probably are cases where, like, it makes sense to create your own. But, like, most most likely you would just use what's in the ecosystem already or provided by, like, the library itself. It's it's nice to see how it works all the way down. Like, you know, now, hopefully, people have the knowledge that they can add the Tokyo Executor to their applications. They, you know, they know that they can then go and create features or use functions

48:50 that return features and how to block and away on these things or let them run asynchronously. And just seeing that it's just a tree implementation with a pull function on it was was quite nice. Like just I mean, I should have thought about it and then, of course, it's Rust. Of course, it's gonna be a trick, but, you know Yes. Yeah. The the Seeing it and it's just really cool. I like that a lot. So It really is cool. I mean, like, I think they've done quite a quite a good job of of the of

49:19 the implementation details and, like, I mean, like, it works. It's also pretty exciting to to do it yourself even if it's something, like, very small like this. Only because it's been seemed like crazy. It's, like, nice to see or to know that it's not the craziest amount of magic. Like, you can do something very simple and, like, put it up your own thing. Yeah. Which is yeah. Which is pretty cool. Cool. So, like, to kind of, like, end off well, it's not end off because it's gonna still take some take some time, but thought it would be cool to, like,

49:50 Writing Our Own Executor

49:56 run through. So Tokyo has its own well, it has, like, a tutorial on, like, making your own executor. And so I thought it would be cool to just kind of, like, walk through that just because it, again, like, gives a bit more context into, like, how things are put together. Oh, definitely. Like, kinda, like, super interesting. Cool. So we'll make a new file here. Cool. So I'm gonna be jumping back and forth just, like, copying code from from that tutorial. So that's on, like it's on the it's on the website. Like, if you go to

50:33 Tokyo.rs and you go to, like, the I think they've got something. Yeah. Learn. They have a, like, a tab called learning. You go to async in-depth. They have a tutorial there. So yeah. I mean, it's it's really I encourage everyone if you're, like, interested in the topic to read through it because it's really it's really interesting. And, I mean, it's obviously a very simple executor, but it's not, like, nearly as complicated as you would have thought it would be, surprisingly. It's it's simple simple examples that teach us. Right? I mean, in a complex example, I'm gonna walk away

51:10 more confused than than I came along. But, yeah, simple works well for me. I like simple. Yeah. No. Same. Yeah. Same. Yeah. Cool. Just give me a sec. Yeah. Take your time. Cool. Alright. Okay. Cool. Sweet. Cool. So yeah. So, yeah, off the off the bat, we'll have this, like so we're gonna we're gonna start off I'll I'll kind of go through it, like, you start off with this, like, executor, and then you have and then we'll kind of, like, build all the constructs going down, which will be fun. Let me just see if I have everything I need here.

51:46 Executor Structure: Task Queue & Channels

52:06 Cool. So, yeah, just off the cuff, we'll have this tequila. And that's so so yeah. First thing that we'll have is, a a task queue, and then let's say trying to think of a of a descriptive name for this. Send to execute a queue. Alright. So we're just gonna use a cross beam. So cross beam is like actually, I don't have a good description of cross beam myself. I'm just I was actually just going let's see if it gives us no. It won't give us anything. Wait. Yeah. That gets me every time we've rust analyze. If you don't use the code, it

53:09 doesn't get all complete or anything. Yeah. That happens throughout the time. Right. So we've got tools for concurrent programming. That's what cross beam is. Okay. Perfect. I know exactly what that is now. Better than any description I could give you, so we're just gonna go with that. Cool. So, yeah, we've got this cross beam channel, and so this will just be a so there's gonna be some details here that I I might just, skip over, honestly, but it's cool. So awesome. Let me put this, like, send the So it's starting to look a bit more

53:55 like go now. Yeah. Yeah. Oh, in channels. So yeah. So we have this. Cool. So yeah. So for this executor, basically, we have so this task will will kind of, like, create a thing next. But, basically, the idea here is that you have an executor. And in our case, we have this task queue. So this is the the, like, queue of task that the executor has to run. So when task gets scheduled on, obviously, backlog the queue, and then, you know, it'll just go through here to figure out, like, what to run next. And then this send to execute a queue, I'm

54:56 just calling it this just because it's easy to understand, like, exactly what it does. But, basically, we're gonna this is the sending side of this of the channel. And so what we want is for a task to be able to, like, send itself onto the task queue. And so we're gonna just, like, pass this down into the task itself in essence. And so that's kind of, like, why it's there. So just adding, like, some methods here. So we're gonna add this in. Cool. So just creating a new new executor. Also so send to executed key.

55:55 So this just created your so this just creates the unbounded channel, so there's no, like, capacity or yeah. No, like, set capacity for it, basically. And then we're just gonna turn that. So Then we have this function called run, which basically was the it's gonna be our entry point. So we're gonna go, like, execute a dot run, and it should basically run all the parts that are there. So this looks like task queue. Even then, they're gonna pull the task itself. So here, all we're gonna do is just, like, run through each task once it's received

56:30 Executor `run` Method

57:10 in the queue and then just pull that task until until it resolves, basically. And then last function. Stuff here. So this actually, you need to figure out exactly why. Oh, so for I mean, this is just a tutorial, so I'm just kinda, like, copying it from there. I'm just typing it up just so everyone can can play along or follow along. But, yeah, it's got, like, the so this is the kinda, like, the one that trade bounce sort of well, yeah, the trade bounce. It's gonna be sent, I think, because of cross beam channel needs to be sent. And

58:20 then this static, I'm not entirely sure. I'm not gonna lie. That's something I haven't I haven't wrapped my head around entirely, but I have a very strong feeling that it's got to do with actually, it probably doesn't have to do with penny. It might. It might. But, yeah, that that's something I'm yet to yet to figure out entirely. And so Yeah. I just throw lifelines around whenever things don't compare and offer the best. I'm just like, yeah. Sure. Static. Go for it. Does it work? Yeah. Done. Literally. Oh, no. This is the wrong thing to

59:04 So it seems here, like, what an executor is is is send and receive channel with a couple of functions to add stuff to the queue and then to run the queue. Yeah. Like yeah. When you break it down like this, you're kinda like, this this makes sense. Like That's really cool. It's really cool. Obviously, this is very watered down, but nonetheless, I mean, that's it's basically like the premise of what's going on. So so yeah. So now we have our executor, yeah, with the queue. And then you can see here in the spawn function, we basically

59:43 pass this, like, send to exit. So, okay, a task firstly, like, wraps a feature and adds a little bit of functionality to it on, like, state management, I guess. And then we're passing this, like, send to execute a queue. What's the number for? Like, sender, I guess. And this the task will be able to use to basically, like, reschedule itself onto the queue. So cool. So that's that. We can then go on to, like, implementing our task. So, yeah, I'm also just gonna copy this in. So cool. Alright. Alright. Let me actually just make a

1:00:14 Implementing the Task Struct

1:00:36 So Will that use statement work with a mod statement? Sorry? You said use create task task, but without a mod. Does that work? Yeah. It does work. As far, I mean, as far as I know. I think it should work. Well, we'll see. We'll see. Yeah. Yeah. If it doesn't, then we can just use mod. Cool. So so, yeah, we have this task. The task is just gonna be a feature, and then that, like, send to execute a queue, basically, which which will be the same as one form. So we've got this and then that takes in a task. And

1:01:53 then this feature is it's just complicated. But basically so it's basically Yeah. A pen and a box. Damn it. Yeah. I don't know. And the and the mutex. And the mutex. Yeah. And the mutex. Yeah. It's The only thing that's missing is ARC, and then it's got pretty much every all the words I hate. So the mutex is to make it sync because cross beam channel requires things to be send and sync. And I think sync is a market trait. Sync and sender market trait. So that means that if all of it's like I forgot what the what the right word

1:02:04 Pin, Box, Mutex: Why are they needed?

1:02:36 is. Like, variables or or whatever that belong to to task, if they are all synced, then task will be synced. If they're all synced, then task will be sent. And, like, if, you know, one of them isn't synced, then the whole thing's not synced, basically. So it's like a it's a auto trade, basically. So this mutex is needed to make it synced. And then on on pinning, pinning is something I'm still also just learning. But in effect, it's it's because Rust features or or yeah. Rust Rust features are they need to be self referential. They need

1:03:13 to have the capability of being self referential so they can refer to their own, like, variables that they constitute of. And so if they if they've moved, then you can imagine, like, let's say you have something sitting at, like, location x and the feature then is moved in memory and it's pointing to itself, then that that part where it's pointing to itself gets invalidated because it's moved in memory. So you wanna pin them so that they don't get moved, basically. So can you remove the move semantics then? Like, keeps it in the same that have been Yeah. Yeah. So it it

1:03:50 basically it basically disallows certain operations from happening on something that's pinned, basically. So yeah. So it it doesn't actually, like, stop anything from technically happening. It just disallows certain functions from from a like, I mean, using certain functions on those on those objects, basically. There you go. There that's the most important thing I think I've learned today. So now we know that we've got this dynamic trait thing of dynamic size thing in memory. So we wrap it in a box. We use a pen to keep it at the same point of memory. So that you refer

1:04:22 to itself with a mutex so that we can control what speaks to it in order. Yeah. Got it. Pretty much. That's pretty much it. Cool. So, yeah. So then we have this task and then we're just gonna implement some of some of its stuff. So okay. First, actually, what could be more interesting is to have this did I I import this? I don't know. Okay. No. I haven't. K. I don't know if I've got this. I can't get added. So let's see. Sweet. So we have this thing called Arc Wake, which basically allows us to implement the that that

1:05:28 Implementing ArcWake for Rescheduling

1:05:35 wake and wake by ref functionality. So that's also a trait. No surprise. And that's and that's needed for for us being able to, like, wake a task and just basically reschedule it back onto the executor. So in our case, we're just gonna implement this wake by red one. May I ask an adjacent question just now? Gotcha. What's the difference between the standard future feature and the creates features? I don't know. Cool. So I I would at least again, from what I know. So when they stabilized, like, the future traits and everything, that's obviously what's in standard in the standard

1:06:23 library. And a lot of the, like, design and, I guess, API development and all that happened externally, similar to, let's say, like, how Tokyo and ASIC standard are kind of, like, core functionalities of how you'd use async Rust, but they're not provided by the standard library. And so that was initially all in this, like, futures crate. But then some of it got stabilized and put into standard. But then there's still a bunch of functionality that's only available in, like, the actual features crate. And so you'd you'd basically use that to add, like, the additional functionality

1:07:01 in essence. Yeah. Okay. Pretty much. So we have this schedule here. And so, basically, what's gonna happen or the premise here is that when we call this wake bar ref or wake, we're basically just gonna schedule it back onto the executor here using this function, which we'll implement now. So so that's scheduled. Oh, so yeah, cool. So we've got this function schedule fix. Oh, and so a weird thing, like, also like a gory implementation sort of like detail. But setting up these, like, wake functions requires kind of, like, some low level unsafe code. And so

1:08:03 an easy way to kind of, like, avoid that is to use this arc wake. I don't I don't understand, like, how it negates or how it solves that problem for you in effect. But, yeah, it's basically kind of like a shortcut from having to deal with routing that, like, unsafe code yourself, basically. Oh, so then we're just gonna also then when when you wanna schedule a task, we basically call the schedule, which will get called by, like, these wakers. And this will this is basically sending it to what what I call this executor send to executor. Yeah. I was gonna say,

1:08:52 where's executor coming from? You just threw me for Just threw away a bunch of it. And so, yeah, we've got the center executed queue, and then we send ourselves basically all this task to that queue. And then I'm actually just gonna copy and paste this. So I'm so then and then we also have this poll functionality here, which yeah. And so effectively, when we when we pull this task also because I've got twenty minutes left. When we pull this task, we basically create a waker and a context, and that context is used when you pull the future internally.

1:09:40 So we've got we've we have, you know, a feature attached to the task. This will never fail purely because no other thread is actually trying to unlock it, so we can just unwrap it. And then we pass that context into into the future itself, which is that which will then get called by that, like, future when when it wants to wake itself up, basically, as well. And then the last part also, I'm just gonna also copy and paste this in, which is that spawning function functionality. So this is actually this is actually quite simple way. When you wanna spawn a task,

1:10:26 we basically create a new yeah. So when you wanna spawn a task, we basically create a new, well, a new task object, and then we send it to oh, this is not sender. This oh, yeah. I thought it is. And then we send this to the the task queue, basically. So that's what gets passed in here. This send to execute a queue. So every time we spawn a task, a task will just basically send itself or or send it a a task to the executed to get to run, basically. So that's that's basically that. And those are the two

1:11:10 kind of, like, big components. And then all we have to all that's left to do is create our own what is this? I have no idea. I will figure that out this time. So, yeah, all that's left to do is, like, create our own future. I also for the sake of time, also just copy and paste that and and just kinda, like, talk through it, which would just be, like, a lot faster. Yeah. Go for it. Cool. But I also need to solve this issue now. This is you're probably right. Yeah. Let's see. I think you still need to use, but

1:11:35 Creating a Future with Actual Delay (FutureDelay)

1:12:02 you need to mod Yeah. Yeah. Yeah. I don't see what's going on here. What's going on? And this whole module. That is super weird. Okay. We'll get back to that. That's probably why. K. Let's see. So oh, I've got a lot of errors coming. It's unresolved import. That should be fine. Cool. Just give me a sec. So what's the error on your used future statement? Oh, it just says unresolved imports. I think it's just not picking up that it's there. Let me see if I can just, like, reload my window and see if that solves any of my issue, which I

1:13:16 doubt, but let's see. Yeah. Yeah. Rust analyzer does seem to flick after a while, and I usually restart Versus code. So Yeah. It does happen every now and again sometimes, but we'll see. Think it's still building? Yeah. Yeah. It's still building a bit. Well k. So It's still giving me ill. I know it's changed. Cool. So that's fine. Here? Yeah. That's definitely happier. Let's see if this works now. Ah, there you go. Ta da. There we go. Back in business. Cool. Sweet. So alright. And then k. We can actually kinda get rid of all of this.

1:14:44 And then there's just this last piece where we have a Let me just get that solved. Curation. Cool. So we have this, like, future delay, which is kind it's almost like a sleep in effect. And so, basically, what happens is I should probably should have written this one out. But, basically, what's happening is we basically wanna delay for a certain amount of time. So we have this thing called the instant. Instant just gives us, like, an instant in time right now. Basically, these are comparisons. And so we wanna say, you know, we want our delay to be,

1:15:41 what's the like, from now plus two seconds, and then only after two seconds will be finished. So in this case, what we have here is if the current time or, like, this current instant is larger than when we specified it to be. So, like, maybe, like, instant plus two seconds from wherever we are, then return call ready. Otherwise, we then go ahead and set it up to basically tell us when to reschedule itself. Here, what happens is, k, we've gotten this the waker from the context, and then we're kind of using, a a semi hackish way, I guess. I don't I

1:16:21 don't know. But we basically spawn a new thread, and then we we sleep that thread for that period of time that's left. So, you know, if we wanted two seconds ahead and we're actually one second ahead, then we wanna sleep for one second. And then once that's done, then we call this waker dot wake. And this is important because if you don't call waker.wake at some point, then your executor will basically hang. Or this task will never get finished or run to completion, purely because it never gets working. It never gets, like, rescheduled onto the executor to run.

1:16:56 And then we have this, like, poll pending in this case. So this will return poll pending, and then once this wake up at wake runs, it'll get executed all again rescheduled and then executed. So that's basically you can ignore those. So this waker. Wake is basically what gets called here, the self dot schedule, which then again sends it to the execute queue. And then I think we're pretty much good to go. So we can go let's actually do this. And then Cool. So we get a new executor, then we gotta create a new feature. So

1:17:39 Running the Custom Executor with a Delay Future

1:18:16 this delay, then we want back an instance. Think you can do this plus. K. I think that should be fine. Cool. And then then we wanna spawn that task, basically. So you wanna spawn the future, and then we want to run, basically. And I think that should do the trick. So this should and when you do a cargo run, create your new executor, create a future that we just created with the delay which just looks for the timestamp offset. You then spawn the future and then hopefully wait and what we should see is yeah. I was gonna say, yeah. Let's get some

1:19:18 principles. Let's say, pending, cool, and then we'll say, we are done. Don't forget the emoji. I think we need, like, a confetti emoji or something. Let me go grab one quickly. Okay. Cool. And then this year, we can actually this will be cool because then we can say, like, we can see that the task was yeah. Making the task back up. I don't know what the k. Let me go get this great emoji from someone. Emojipedia. That's a good one. There's a shortcut on the Mac for emojis, and I can't remember. Oh, really? Yeah. I

1:20:06 can't remember what it is because I've got mine set up as the function key, but I think that's option. But I think you can do the command shift and something. I'm gonna look it up. Completely irrelevant though, but I'm gonna look it up. K. Cool. Yeah. So we should expect it to pin then to complete that, say, waiting the task, then we are ready. Hopefully, let's see how it goes. If you had a drum roll. Where in the what happened to my which I don't know where I am in my computer. K. I think we're at the right place

1:20:48 now. It's control command and space bar for future reference. There we go. Cool. Cool. Oh, oh, now just to wait. I know. The fastest compiler ever. It's it's I do like that they do make speed improvements. I do it's it's easy to make fun of the Rust compiler, and I do it often. But I also appreciate that it's a complex piece of software, and people are content. They're trying to make it better. So %. I mean, there's that also. It gives you a lot of guarantees. Like, it's a it's a basically a kind of like trade off between what it offers you

1:21:25 versus like how fast Yeah. Definitely. It compiles. So yeah. It's it's alright. Let's see what happens. K. Alright. Pending my task ready. Cool. But then It's still hot. I don't know. It's still hot. I have no idea why. Oh, no. That makes no. That makes sense because this is this is a blocking this thing is still running. Alright. Alright. Okay. And this blocks and this blocks. So it worked, basically. So I guess if we added that I mean, we don't need to do it. But if if we checked in the task queue is empty, we could just exit.

1:21:59 Right? Yeah. Yeah. Something like that. Yeah. We could we could do that. But in theory, like, let's say if you're running a a web server, you wouldn't wanna do that for instance because you can get traffic at any point in time. Yeah. May not be a very good web server. Cool. So, yeah, that's basically that's basically that. Yeah. That's that's everything from my side. I'll pop with I hope that was enjoyable. No. That was that was great. Like, I I think, you know, going over all the primitives and then seeing how they're actually implemented under the

1:22:17 Conclusion and Wrap-up

1:22:30 hood is, you know, like, it's not wildly complicated even though it can feel like it. And I think you did a very great job of just breaking it down and showing us the primitives that we have to work with. And it really for me personally just cemented a lot of those things that I had big question marks on. And I'm like, oh, those now make a lot more sense to me. To me that was a really valuable demo. I thought that was great. And I know Awesome. How the executors work. It used to be something I would try away from.

1:22:57 Yeah. That's like the best function that has a pull on it and stuff like that. So very, useful. Thank you. Awesome. Thanks. Alright. Well, we are approaching time. We got a lovely comment there from Russell saying that was an awesome demo. Thank you very much, Samuel and David. I mean, I'll take some of the credit for sure, but it was definitely a % all Samuel there. So thank you for taking time out of your day, for joining us, and for doing live coding, which I know is no easy feat. So Not at all. Especially my first time, so it wasn't too

1:23:29 long. Oh, damn it. Oh, well, you did great. Thank you very much. Thank you. Alright. Well, I'll let you get back to your day. Any final words before we say goodbye? Cool. I'm good. That's awesome. Alright. Awesome. I'll speak to you again soon. Have a wonderful day. Thank you all for watching. Bye. Yeah.

Technologies featured

Meet the Cast

Weekly Cloud Native insights

Stay ahead in cloud native

Tutorials, deep dives, and curated events. No fluff.

Comments, transcript, and resources

More from Rawkode Live

View all 173 episodes
Rust

More about Rust

View all 22 videos