The Rustacean Station Podcast

What's new in Rust 1.38

Back to Episode Page

Ben Striegel: Welcome once again, ladies and gentlemen, to Rustacean Station. I am Ben Striegel.

Jon Gjengset: And I am Jon Gjengset, and we are excited to tell you about the 1.38 release of Rust.

Ben: It’s been a while. Don’t need to rush right into it. I don’t think this is going to be a super long episode this time.

Jon: Are you saying 1.38 is not as exciting as 1.39 will be?

Ben: No, no no no. They’re all exciting. They’re all— every Rust release is precious in its own way.

Jon: Are they all equally exciting?

Ben: Not really. I don’t know, I guess, But still, there’s, like, it shouldn’t be a—

Jon: Competition?

Ben: A big momentous thing. We don’t want to try to, like, rank our children here.

Jon: That’s true. I mean, it is, like we discussed a little bit last episode, it is also— this is one of the nice things about the Rust release cycle, is that you can have releases that are really exciting, and some that are smaller ones. But at least you’re always making progress.

Ben: I think in this one, the biggest, the most exciting feature for most folks is going to be, like, potential compilation time improvements, for projects with large crate graphs. They’re calling this one “pipelined compilation,” and it is a new feature. It is on by default, I believe. So if you’re using the newest stable release, you should be using it already. Do you want to go into what this means, Jon, or what you know about this?

Jon: Yeah, so this is pretty cool. I mean, this is a complaint that we hear a lot in the Rust world, of the compiler takes forever to compile my thing. And one way to at least help with this problem is to compile more things in parallel. Now previously, the Rust compiler would, or Cargo would compile each crate sort of separately. So if you had some crate C that depends on both A and B, it would fully compile A than fully compile— or fully compile A and B, and then it would fully compile C. But it’s not— it turns out that’s not really necessary. Instead, you can compile the metadata for A and B, and then you can compile C straightaway after, in parallel with compiling the sort of contents of A and B.

Ben: But what is metadata? You can’t just gloss over that.

Jon: So metadata is stuff like figuring out the types, figuring out what the functions are without necessarily compiling the body of them, like, without running things like optimizations.

Ben: So kind of like header files, almost.

Jon: Almost sort of like header files, yeah. All you really need is, you need to know all of the external parts of the crate, because those are the things that the other crates are going to use. And then, you only really need the final optimized compiled artifacts at the end, when you need to link them together.

Ben: Yeah, so previously, any Rust crate is its own compilation unit, and anything that depends on it has to wait for it to be done before it can start. But I mean, again, like, you don’t need to know everything about a compiled crate to actually begin compiling. And so this, kind of, just lets us kind of shortcut the process.

Jon: Yeah, you can think of this as, you don’t need the assembly code for all of the crates you’re depending on, in order to compile your crate. Because much of it will be function calls into some part of the code that is external to the current crate. And then this is where link time optimization comes in. Where at link time, you can then try to do optimization across crate boundaries.

The other thing, that’s a little neat, as we talked about back in 1.36, the “mem uninit” type landed. (editor’s note: should be MaybeUninit.) And this was to try to deal with some issues around incorrect use of mem::uninitialized.

Ben: And if you haven’t listened to our episode on that, feel free to do that right now, our very first episode.

Jon: It’s okay, we’ll wait.

Ben: We’ll wait. It’s about an hour long.

All right, you’ve listened to it now, and now you’re back. So thanks.

Jon: Yeah, exactly. Good job. I’m impressed. By the listener. Not you, Ben.

Ben: Yeah, I’m not impressive.

Jon: Well, sometimes.

Ben: Go on. So, “mem uninit”.

Jon: Yeah. So it turns out that uninitialized is broken, and it’s very easy to do things that are incorrect with mem::uninitialized.

Ben: In fact, it’s almost— it’s hard to do things that aren’t incorrect. Which is even worse.

Jon: And part of the reason for this is, for any given Rust type, there may be bit patterns that are not permitted. The simplest of these is for— if you have a reference type in Rust, then references in Rust are not allowed to be null. So that is, their bits are not allowed to be all zeros. So if you have a type that contains a reference and you use mem::uninitialized to create it, then that creates something that might have a reference whose bits are all zero, which is undefined behavior in Rust and so is not okay. Similarly, if you have a boolean whose bit value is not 0 or 1, that is also not okay, and might cause undefined behavior. And so what’s happened with the 1.38 release is that now, the compiler will tell you if you try to use mem::uninitialized to create one of these types. Even deep down in nested structs. The same is the case for a type like Box, right, so a Box has to be a non-null pointer. So if you have a type that contains a Box, and you try to create it with mem::uninitialized, the compiler will now warn you that this is not okay, and is undefined behavior.

Ben: So it is kind of on the path to actual deprecation, uninitialized. Not just yet, because it’s kind of a case where you want to give people time to actually update their code. And so in this kind of interim period, people should begin migrating to MaybeUninit, which is the new replacement for it. But in the meantime, you can still be using this, cause it’s not like it’s impossible to use correctly, mem::uninitialized. But it is just highly likely. And so this new lint is, kind of, things that obviously wrong. We can’t lint against everything that could be, like, potentially wrong. Obviously, if we could, we could just not have this be unsafe at all, in the slightest. There’s not really any timeline for deprecation for this right now. So I think it’s just kind of like, seeing how quickly people update their code, and that kind of thing. So, but look forward to that being actually fully deprecated at some point in the future.

Jon: And speaking of deprecation, there’s also a new use of the deprecation macro attribute that just landed. Do you want to talk a little bit about that?

Ben: Yeah, sure. So Rust, actually, if you’ve ever, for example, like, I mentioned deprecation, the way that the standard library deprecates things is actually quite simple. You just put an attribute on the function. And so if you’ve ever used, like, derive, that’s an attribute, you just, like, plop it on there. And now you have things happening for free. The deprecated attribute on functions, essentially, all it does is, any time anyone uses this function in their code, the compiler will say, hey, like, by the way, this function is deprecated. And as the author of the function, you can also attach, like, notes saying, hey, look, here’s why it’s deprecated, here’s what you should use instead, you can say this has been deprecated since this release so-and-so. And a long time ago, this became not just a feature of the Rust developers, but also anyone can use this in their own libraries, too. But previously it was restricted to things that weren’t macros, and now it works on macros as well. So kind of, more steps towards making macros, kind of, feel less like their own kind of like, cloistered off part of the language. More like first class, say, or more integrated with the rest of how Rust normally works. So less special cases.

I wanted to also real quick, kind of, mention, you can’t, like, if you were curious to know more about the deprecated macro, you can’t just go to, like, the Rust API docs and, like, search for deprecated, say, it is— as kind of built-in compiler attribute, you actually have to go to the Rust reference, which is, if you look in the reference under the— where did I have that here somewhere? Well, I’ve lost it, but it was the diagnostics category under the attributes section. If you look under there, you can see information. Also, if you want to know about any of the other built-in macros in Rust like derive, say, you should also check the reference for that kind of thing. It would be nice if this showed up in the API docs, because built-in attributes are kind of indistinguishable from a procedural macro, or like another thing that you might expect to be in API documentation, and I think you were saying how even if you your own procedural macros nowadays, they won’t show up in the API docs.

Jon: Yeah, So this gets into the different types of procedural macros there are. So there are procedural macros that generate function-like macros. These are the same things you get if you used the macro_rules!, the, like, declarative macros from before.

Ben: Like a println!.

Jon: Yeah. So, println!, format!, all of these are—

Ben: Name, then bang. And then some arguments inside parentheses and then, just like, you know, it looks like a normal, like, you know, syntax macro.

Jon: Yeah. Exactly.

Ben: The procedural, full blown shebang.

Jon: Yeah, and all of these function-like macros are already generated by rustdoc, and they’re listed normally in the documentation. But things like derived macros, and other kinds of attribute macros, such as— or attributes at all. Such as deprecated here, those, even though I don’t know whether that’s implemented as a macro internally in the compiler, but it is an attribute that you can use, and those I don’t think are generated in the docs at all. And that’s also why they don’t show up in the documentation for the standard library.

Ben: So that would be very nice to have, just for user convenience. Did I cut you off before? Did you want to say—

Jon: No. I just wanted to say that this feels a bit to me like, similar change to being able to name macros in use statements, of just making macros feel more like first class citizens, alongside other things like types and traits and functions.

The other thing I wanted to mention briefly, when we talk about deprecation, is that I’ve seen a number of crates use #![deny(warnings)] to make sure that, well, to essentially turn every warning into an error. This is a common pattern in other languages as well, where you use -Wall -Werror in C, for example. And in Rust, you have to be a little bit careful about doing this, because it means that even warnings about using deprecated methods also turn into hard errors. And this means that a semantic versioning compatible bump of a dependency might cause your crate to start issue warnings, which then become hard errors.

Ben: If you have this deny—

Jon: #![deny(warnings)].

Ben: You have all warnings denied.

Jon: Yeah.

Ben: I think even the Rust team themselves, like for the purpose of like, Crater runs— and so Crater is a tool that just checks new versions of the compiler against all the crates on crates.io to see if there are any, like, problems, and it actually ignores the deny warnings lint because of this, and so it’s kind of like, you could just, if you want, enable this. It’s not a big deal, like, deprecation is just a thing.

Jon: And it’s because it means that semantically, semver-compatible updates of dependencies might now break your build, even though the update was, in fact, completely semver-compatible.

There’s another thing that landed in 1.38 which is this std::any::type_name. So this is primarily used in debug context. So the idea here is that it’s generic over some type T just like the size_of function, for example, and it just gives you a string that describes the type you’re talking about. If you run type_name, generic over bool, then you’ll get back a string that just says "bool". And the idea is, you can use this to print out your types, if you don’t necessarily care about all the stuff that’s inside them. Or if you want to print out a type that is not Debug, than you can at least print its name. This is also handy for macro authors. Like we talked about last episodes, there are a couple of these kind of changes that we’re seeing in the language that might not make a lot of sense for people who aren’t writing macros. But if you are writing macros, then being able to just easily get at the name of a type is pretty handy. This also has the flavor of something we talked about a little bit in the past, and something that happened in the 1.34 release, which was this type_id debacle.

Ben: Which predates our podcast. But I think we mentioned it in passing, where in Rust 1.34 we stabilized— we as in everyone.

Jon: Ben and I.

Ben: Jon and I, actually yes, we decided in our wisdom to stabilize the type_id field, method, or— on the Error type, actually.

Jon: Yeah, it was a combination of a trait and a downcast method.

Ben: Yeah. So it was a function defined on the Error type in the standard library called type_id. And the idea was that this would give you kind of a unique identifier for whatever type was contained within. And there was a problem— it was actually a security advisory— about this. On the security mailing list.

Jon: Yeah. The problem there was really that the compiler— or, the code generated by Rust or the safety guarantees, relied on a type faithfully giving the TypeId that the compiler had assigned to that type. But because type_id was a trait you could override, you could override it and just give some random, other TypeId. And now you could downcast basically any type to any other type.

Ben: Which is bad.

Jon: Yeah. It turns out that—

Ben: Bad for memory safety.

Jon: Yeah, it turns out that that is actually a bad idea. Believe it or not.

Ben: Yeah. And so, actually, that’s one of the examples of something that was de-stabilized. I’m looking here at the time line for the security advisory here, where I think about a month after it had been released. It was actually, like, taken out of language entirely in a point release, saying, actually, there’s no way we can make this safe, because of the combination of these few features here. If you were to do this, it would cause instability. And so while it’s very unlikely that anybody actually did this, you just can’t have that kind of hole lying around. So yeah, a case where deprecation was not considered and there’s no, like, light touch. It was no, this has to go immediately. There’s just no way this can be safe.

Jon: Yeah, we don’t have too many of these in Rust land, where we decide something has to go away.

Ben: Even like the “mem uninit” we mentioned before. Like, even though like, it’s almost— it’s considered in some cases, almost impossible to use correctly, if you use it on any kind of generic type. That wasn’t even like, you know, hard removed, even that we could remove it at any point. But now that we have the replacement. But type_id was just like, nope, it’s gotta go.

Jon: The only other one I can really think of is, I remember in the early days, there was a thread::scoped in Rust. You could pass it a closure to run—

Ben: Before 1.0? Was this the—

Jon: Yeah, I think it was nightly only for— I forget exactly.

Ben: There was one case where we had a thing stabilized for one day. And then Huon Wilson was like, hey, I was reading the release notes. And actually, you have the wrong type signature for this thing. And so we to actually, like, the day after. Someone had just forgotten it was an unsafe API, and somebody had meant to write a mutable reference when they wrote a shared reference or something. It was like, oops!

Jon: Also, thread::scoped went away entirely, because there wasn’t—

Ben: I think this was before 1.0, though, which changes the thing entirely.

Jon: Yeah, I think you’re right.

Ben: Before 1.0 was a primordial time. Things changed quickly.

Jon: A giant primordial soup.

Ben: Yeah. Those are all the meaningful language changes this time around. Or, I guess there was actually was a library change. But we have more library changes, so—

Jon: Yes, we have some smaller ones. So one of them is— and some of you may know about this method from before, but if you have a slice, or anything that derefs to a slice, then you can join them with some separators. So the idea here is— the example that comes most often to mind is, you have a string or you have a slice of strings and you want to join them by some separator. So this is usually something like a comma. You want a list to be comma separated as a single string, and you can do the same thing for a slice. You have a slice of u8s and you want to join those many slices of u8s by some separator, and previously you could only join them by a single separator, so a single u8. Whereas now, with this new change, you can join by something that is a slice itself, so you might join by something like 0, 1, 2.

Ben: Yeah, I think if you think about it in terms of like, we’re familiar with the join method on things where it’s like, you know, we just have a list of numbers. You want to join it by a comma, and make a string out of them or something. It doesn’t make a lot of sense, you think to yourself, well, why wouldn’t I just, like, put, you know, the other things inside of the string that I want to join by. Well, if you have, like a number and you have— I want this list of numbers to be separated by, like, you know, 100 zeros or 100 fives or whatever, it’s like, that isn’t a valid u8, obviously. So you couldn’t just do that. And now you can have any arbitrary thing inside, to separate your various items.

Jon: And there you also see that we have this connect and join. And connect

Ben: Yeah, again, we get back in the deprecation thing around, like, early days of Rust, we stabilized a connect function, which— and then people were like, actually, every other language calls this join, just to be, like, familiar with everyone else, we should just call it join. And so that was deprecated. It’s still around. It was again, still being supported, it has this new support as well. But nowadays you should be calling the join function. It’s just not a big deal to remove from the language, because it is deprecated, and it will give a warning if you try to use it.

Jon: And if you don’t call join you’re a dummy.

Ben: No, no, everyone who wants to use the thing. It’s fine, you know, it’s different strokes for different folks.

Jon: That’s true. That’s true. There’s another change that might seem a little weird at first glance, and that is that raw pointers will now implement Unpin for any T.

Ben: I think Pin is kind of a complex subject. Is there a way that you could summarize briefly what this means?

Jon: So last time I tried to summarize Pin and Unpin, it ended up being a three hour long video. But I will try my best. So the idea with the Pin and Unpin types is that if you surround a type in Pin, it means that you are promising that you will never move that type again. So once you place it behind a Pin, that thing will never move in memory.

Ben: That location in memory forever.

Jon: Yes, until it gets dropped. And Unpin means, I don’t care about that contract. So if you put a type T inside of a Pin, you can’t get that T out of the Pin again unless that T is Unpin. And most types in Rust are Unpin, like, u8 doesn’t care whether you—

Ben: Most types don’t care if they—

Jon: And the few types that do are generally things like self-referential structs, which you normally can’t write in Rust. But they can be constructed for you, if you use things like async fns.

Ben: And the reason this exists is for, things that most matter, are— you mentioned these self-referential structs, which includes generators, which are being generated by the upcoming async/await syntax, which is kind of like— the precursor to this was, well, we need a way for these generators to actually contain references to themselves somehow, and so that’s what this is for. Most users don’t care about Pin. I think they probably shouldn’t. It’s more a library implementer kind of thing. And in this case, we’re just adding it to these raw pointers here. I guess the reasoning was, why not? Do you know the exact reason?

Jon: Yeah. So the reasoning here is that most types should be Unpin. And some types are not Unpin, like we talked about. Some things are Unpin for interesting reasons, like a Box<T> is Unpin even if T is not Unpin. And the reason for this is, if you move a Box, the T behind that Box does not move. And so therefore, you can move that Box freely, and the type T will still stay in the same place. And the argument here is, the same thing applies to raw pointers. If you move the pointer, that doesn’t move the T. And therefore the pointers should be Unpin even if the T is not Unpin.

Ben: And was it just an abundance of caution that caused these to not be originally marked as Unpin?

Jon: I think that’s the case. That originally, it was like, well, who knows about raw pointers, really? They’re all sorts of complicated. And then the realization was, they should be fine because they’re just like heap pointers.

Ben: Okay.

Jon: There’s also a really exciting new change that only matters perhaps for a small group of people.

Ben: The Duration.

Jon: Yeah. So if you have a Duration, you can now call as_secs_f32 and as_secs_f64, which gives you a floating point number for the number of seconds of that duration. And this is really handy if you want to do things like, just print out how long something took, maybe in seconds, maybe in some other unit, but you want to print it out with decimal places. Previously, you had a way to get the number of seconds and the number of nanoseconds, and in some cases, like the number of millis and and micros landed recently. But now you can just get a single number, which is an f32 or an f64, which is the seconds, and the decimals. And this is handy for printing out, especially if you’re doing benchmarks and you want to do things like compute throughput. That’s a lot easier now, because you can get the time taken as an f64.

Ben: It’s kind of more convenience methods being added pretty quickly to this Duration type. Which was kind of just like, bare bones for quite a while.

Jon: Yeah.

Ben: So, pretty nice. Nice little ergonomic change.

Jon: Speaking of ergonomic changes, we have some interesting things coming up in the next— or, it’s already in 1.38 but they’re nightly-only for now. And those are, in particular, Cargo features that are really nice and hopefully we’ll be able to use on stable Cargo pretty soon. The first of these is, you can now use cargo fix --clippy. And this is something that I know a lot of people have wanted for a while.

Ben: What’s cargo fix?

Jon: So cargo fix is a sort of automated tool that cargo comes with, which is, if the compiler tells you this doesn’t compile, try this instead. cargo fix basically automatically applies those changes for you.

Ben: It’s very nice.

Jon: You can think of this as, how many times have you had the compiler tell you, “do this instead” and you’re like, well, why don’t you do it? And that this is exactly that. cargo fix will just do it. And now there’s a --clippy flag which is, run clippy, and if clippy suggests the fix, just make that fix for me. Very convenient.

We also, speaking of clippy, there’s also a nightly Cargo change that landed in 1.38, and that is, if you run clippy twice in a row, you will now still get the clippy warnings the second time you run it. Previously, we had this problem where, if you compiled your crate and then you ran clippy, you would get no warnings, because clippy would be like, oh, there’s nothing to compile. Everything is up to date. And that’s obviously not really what you want when you run clippy, you want the warnings. And so now clippy will re-compile your crate, even if you have previously compiled it.

Ben: There’s something else about a Cargo.lock here, you mentioned, which I haven’t seen. But you have some information about.

Jon: Yeah. So this is is not even nightly-only. This is sort of a hidden feature, so to speak.

Ben: That became stabilized with the 1.38 release.

Jon: Yeah. So now Cargo ships with support for an updated Cargo.lock format, and this updated format is disabled by default. And all the release notes say, is really that it will use it if it sees it. It’s unclear how you generate it in the first place, but this new Cargo.lock format is supposed to be diff-friendly. So if you check them into version control, rather than now, getting like, 100 line changes because you ran cargo update. Presumably you should get fewer changes.

Ben: Which is nice, because at my company, we actually do— well, we just began checking in our Cargo.lock to our big monorepo, the thing that we use Rust for. And GitHub, GitHub is actually smart enough to know that Cargo.lock is a generated file, and will not show the diff by default, even if it’s small. But if you want to just do a git diff in your command line, it’s nice to be like, okay, just scroll past all the Cargo.lock changes. Okay, here we go. The actual code.

Jon: Yeah, And it’s particularly annoying, because the Cargo.lock file is usually at the top, like it sorts first.

Ben: And it’s huge. Because it has all kinds of, like, you know, hashes about things and various like, data about every single crate that you want to compile.

Jon: Yeah, so I don’t know what this new format actually looks like, but I spotted them in, like, the detailed release notes, and this seems like something that would be pretty welcome.

We also have some exciting beta changes.

Ben: Yes, that’s it for 1.38. We can kind of talk about— so we, I think a while back, probably one of our first episodes, you mention how, 1.38 was the original target for async/await syntax to hit stable. That was pushed back by one release, kind of missed the cutoff by a week or so. But not a big deal. Six more weeks until it hits stable. It’s available for all to use. And in the meantime, there are plenty of libraries who are excitedly preparing for this. And so, I think we mentioned before, but async/await is not, kind of, the end- all be-all of the async story in Rust. You can’t be like, yeah, I have this new 1.39 compiler. What do I do with it? If you actually want to use it in a serious capacity, most folks who care about this, which is not everyone, if you don’t care about async/await, then that’s fine. But if you do, it’s probably because you’re using kind of web framework or it’s something with the web, or maybe some kind of, like, backend service that’s very I/O heavy with SQL calls, or other kinds of random IO that you’re doing. And so in this case, you want libraries to actually support this. And so there’s quite a deep list of libraries that are needed to make this very nice. And so, probably the futures library is the first one. And then on top of futures, there is tokio, which actually manages to schedule all of your futures. And then there is hyper, which lets you use tokio to make HTTP calls. And then, you may be using a web framework on top of that. And so at least, kind of, four libraries that you would want to have stabilized, with new supported releases, before you could really say, hey, I’m using async/await in a really official, like, production capacity. And so the good news is that futures and tokio and hyper all have alpha releases currently, where they’re preparing for async/await to become stable. And so I think the goal, at least for the futures, is certainly to be to have its release before the 1.39 release comes out.

Jon: Yeah, I think the library authors here have been really good at keeping up. And I think keeping up with each other as well. So when the new, I think alpha-19 was released of futures pretty recently, and then tokio released a new alpha right after that, and then hyper released a new alpha right after that. So there’s a pretty good coordination in the ecosystem. And I think the idea is, that all of these are going to land stable versions the moment the— essentially the current beta becomes the 1.39 stable.

Ben: And do we have a date for that? I could look up my calendar here if you want. Just distract the audience for a few seconds.

Jon: Yeah, I forget exactly when the 1.39 release is supposed to drop. I think the current expectation is that async/await will in fact land. It is in beta now, so that should be the case. There are still some known issues, but they’re known issues more in the sense of things to be improved in the future.

Ben: November 7th, is what I have in my calendar.

Jon: November 7th.

Ben: Yeah, so that should be the day that 1.39 lands and async/await syntax becomes stable. And so hopefully by then, the goal is that all of these fundamental libraries should become stable. And I think the bigger question now is, like, I think, so futures, tokio and hyper should all be stable by then with new releases. But web frameworks, if you want to just not issue like raw HTTP calls with hyper, if you want to do things with actual, like, routing and perimeter handling and all this kind of stuff, you won’t have a framework for that. And are they going to support this, and have— like, work on the new releases of these underlying libraries? I know that Warp has a PR currently working on that. I think you said Rocket?

Jon: Yeah. Rocket now uses alphas of tokio, hyper and async-std, I think. Not entirely clear to me why they use multiple runtimes, but they are. And there’s an issue that’s open on basically rounding out and finishing off that support for async/await, which is issue 1065, if anyone wants to look it up and look at all the discussion that’s happening there. There’s also Tower. So all the Tower stuff, which is from the— some of the same team that develops Tokio—

Ben: Which doesn’t include Tower Web, which is their web framework, and was only kind of a proof of concept of Tower itself. I think right now it is currently unmaintained. They’re saying you should use Warp instead.

Jon: Yeah. But Hyper, for example, now uses Tower.

Ben: Yeah.

Jon: And so the Tower stack, I think, is also on std::future. And it’s also aiming for, like, a stable release.

Ben: Forgot about that, yeah.

Jon: I don’t know about any other— I don’t know about Actix Web and how their progress is on std::future. I’m not sure.

Ben: And then, there are probably things like Diesel as well, where if you want to have SQL calls happening asynchronously, I’m not sure how they’re doing right now. So we should have some kind of progress report hopefully, as the weeks roll on and this becomes closer. I mean, we’re recording this a bit late. Life gets in the way sometimes. Not a big deal, but it’s today, October ninth or so.

Jon: Yeah. So it’s about a month.

Ben: So we have a month out from, like, four weeks from here, you’ll be listening to this a few days after this, record it so— ever closer.

Jon: I also know that the— at least the mysql_async crate also has an alpha out for std::future.

Ben: I also saw that reqwest as well. And so, the kind of, like, the easy version of Hyper that’s not a web programmer, just kind of issue HTTP calls. But do it in a very convenient way. And that has a new async release alpha, I believe.

Jon: Yeah. So the ecosystem really is keeping up with this. And I think we’re going to expect basically all of them to issue new major releases the moment that 1.39 hits.

Ben: And thanks to all the folks who are contributing to these async things, which also includes our own Jon Gjengset here, I see you’re— I’m always, like, looking around and like, oh, it’s Jon’s name’s always appear in the release notes or in the commit log.

Jon: It’s funny because I accidentally signed up to Hacktoberfest, then immediately succeeded at the, like, you need to issue so-and-so many PR’s.

Ben: Nice. Okay.

Jon: Yeah, I think one thing that will be interesting with the 1.39 release is that the whole ecosystem is suddenly going to have a minimum version requirement of 1.39.

Ben: Yeah, that’s a good thing to think about.

Jon: It’s its own kind of interesting, that suddenly if you do anything in, like, the networking space, like, you’re going to have to need to be on 1.39 or or a recent beta nightly.

Ben: Which shouldn’t be too much of a problem, for I think people who are doing async/await stuff in Rust right now, we’re pretty used to tracking bleeding edge things. I don’t know, we’ll see. I know there are like, the big production users, like, you know, Google’s Fuchsia project and Microsoft. There’s a bunch of like, there’s a reason that Tokio has been so reluctant to make breaking changes because they have many large users. But it’s important to also have those users because they can also test out like, hey, does this is actually work? Are you, like, meeting your promises, and that kind of thing, in terms of performance and ergonomics.

Jon: In some ways, this is an opportunity for the ecosystem too, right? Like now, we’re going to do a synchronized release, basically, where everyone is going to issue breaking changes at once, and so now’s a chance to do some of the larger ecosystem changes.

Ben: And again, if you don’t care about async/await, then it’s not a big deal for you.

Jon: Yeah, then you can just—

Ben: If you aren’t doing web stuff, or I/O-bound stuff, if you’re just doing normal CPU-bound tasks, then it’s kind of like, yeah, cool, whatever. New future.

Jon: I think that’s all we had for the 1.38 release.

Ben: It’s all I had.

Jon: It was a small release, but I think it’s definitely progress. I think all of these are good changes, and it shows that the language is developing and maturing.

Ben: And even, like, you know, small changes, but under the hood, there are still, like plenty of things, that have gone towards supporting again, like all the generator work, async/await, like, there’s always work going on, like new groundwork being laid for things to come the next year, even with regard to const fn and various things with the trait resolver, Chalk, the like, expanded, non-lexical lifetimes, the Polonius. And so, various secret projects are always progressing in the background. So the machinery is all there, just lying in wait, preparing to be used, getting tested.

Jon: Yeah, it’s really cool. All right. I think with that, we’re going to— are we signing off?

Ben: I’m signing off. See ya.

Jon: Yeah.

Ben: All right, bye.