54 comments

[ 1.6 ms ] story [ 109 ms ] thread
The big thing in this one is the LLVM upgrade, which has been a long time coming. :) We actually skipped LLVM 5 entirely due to some issues with emscripten. It'll be great to finally be able to experiment with LLD.

Looking forward, the next two releases, 1.26 and 1.27, are going to have some major features that we've been working on for a long, long time, such as "impl trait" (which should allow for improved performance, simpler syntax for the typical use of generics, and greatly improve error messages for extensive use of generics), more ergonomic matching when dealing with references, 128-bit integers, stable SIMD library support, and others. This whole year is going to see the continued stabilization of the ergonomics intiatives from last year, so stay tuned for lots of welcome tweaks to make Rust easier to write and read. :)

(I should also mention that this release happened live from the Rust All-Hands happening in Berlin, which has been a massively productive time so far; keep an eye out for the eventual workweek recap which will hopefully enumerate the tons of improvements (e.g. reducing Rust binary sizes by 10%) and future initiatives (e.g. forming an official Verification Team) that have happened this week.)

I love this focus on ergonomics in the Rust community. Recently I had to work with a Rust code base and I was amazed by how clean the tooling is compared to other low-level language stacks (C, C++ basically).
>stable SIMD library support

Oh, that's a huge one for me and I didn't even know it was in the pipeline, very happy to hear that. Now I'll have to complain about lack of stable inline ASM support instead!

You can check it out in nightly here https://doc.rust-lang.org/nightly/std/arch/

> Now I'll have to complain about lack of stable inline ASM support instead!

This one is much, much harder.

>This one is much, much harder.

I'm curious why that is, intuitively I'd have assumed that SIMD support would be trickier to stabilize (since you need a generic interface over a wide range of implementations) while inline ASM is pretty much "just copy-paste this bit of assembly in the middle of this function" which I assume is already something LLVM can do easily since it needs that for C/C++. Since nobody expects inline ASM to be portable you don't have to worry about making one single generic interface to it.

What's the catch?

w.r.t. to SIMD, there are two prongs to it:

* Platform dependent functions that match an upstream vendor's API. (Think __m128i and friends.)

* Platform independent APIs that provide portable vector APIs.

The former is necessary because the latter cannot ever hope to cover all use cases supported by CPUs. The latter is necessary because it can hit many use cases, and finding the optimal implementation for every operation for every permutation of platform (and every permutation of CPU feature on each platform) is a non-trivial task.

So yeah, that is indeed a ton of work. However, LLVM does a lot of the heavy lifting for us, particularly with respect to the implementation of the portable API. "All" we have to do is define an API we're willing to stabilize in `std`. (Which is no easy task.)

I'm less in touch with the inline ASM feature, but like most things, I expect it is much more complex than you're suggesting. You can read more about it on the tracking issue for inline ASM[1]. Remember, if we're stabilizing something, then we need to make sure it continues to work. If LLVM's doesn't uphold that guarantee, that we must necessarily provide our own interface that is stable. "Just expose LLVM's internals" is not really an acceptable answer, and it was in fact a large part of the SIMD effort (building APIs we can stabilize on top of what LLVM supports).

I'd also like to note that the stabilization of the former piece above (platform dependent vendor intrinsics) eats into many of the use cases for inline ASM. Not all of course, but many. :-)

[1] - https://github.com/rust-lang/rust/issues/29722

Very insightful, thank you. Reading through the github discussion I'm also glad to see that there seems to be a consensus for not adopting gcc's atrocious inline ASM syntax...
Yes, the fact that we basically just do what LLVM does, and that that's not stable, is a huge blocker. There's also the broader question of "DSL" vs the way that LLVM does it...
> It'll be great to finally be able to experiment with LLD.

Was the lack of lld the only reason for the Windows installer to depend on the MSVC build tools, or are there others?

There are others. If you want to compile C code to include in your project, you'll need those tools. LLD and link.exe aren't exactly at feature parity, etc.

I'm most excited about LLD for hobby OS dev; with Rust and LLD it's now trivial to get going on any OS Rust supports. As a Windows user, I really appreciate it!

> If you want to compile C code to include in your project, you'll need those tools.

Just curious, but if you're already built on top of llvm, why not bundle clang for that? Licensing issues? I thought NCSA was pretty permissive.

That works if you can actually compile the code yourself, I left out the "if you want to link to a pre-compiled library that was compiled with MSVC" case.

Basically, MSVC is the default, standard way to access C code on the platform, so we work with it by default. We also provide other options if you want to do it other ways, through things like the GNU target.

Ah, gotcha. Thanks for clarifying.
Every release that doesn't have impl Trait is a bit of a disappointment, but I appreciate it has to be done right.

So excited for that one single improvement, it's going to make many things so much easier. Especially working with Futures and Iterators.

Well, you've got six weeks to wait :)
It's in 1.26??

YES!

YES!

WOOOOO!!!

That's basically my birthday present.

Yup! In both argument and return positions; in trait methods isn't in yet.

1.26 and 1.27 are gonna be really big, I wasn't kidding :)

Is anyone keeping something like [1] up-to-date? It's nice to able to see what's coming up in the next releases, for those of us who don't follow Rust closely enough to navigate the issue soup.

[1]: https://internals.rust-lang.org/t/rust-release-milestone-pre...

Not in general, but for Rust 2018, the edition release, we have https://docs.google.com/spreadsheets/d/188xuIgrzGLCu68UKpRjc... (note the tabs too)

It is, of course, best-effort, and often guestimates, so please don't take it as gospel, but describes what we're trying to do. (There was already one situation where a mistake in the spreadsheet caused someone to be misled about when a feature is going to be available)

Both positions! Woooo!

Trait methods, eh, we'll just have to live without that for now, just making it easy to return iterator chains from functions is a massive thing for me and a huge boost to the usability of the language and the practical value of trait abstractions.

But you know that, of course. I'm just excited.

Now very interested in what 1.27 will have in it! I haven't tripped over the lack of NLL for a while, but that's been another thing I've been eagerly awaiting because I used to and I've just trained myself out of it.

Again, I gotta repeat: this is not a promise. Development on 1.26 is just starting, so this is a reasonably far out estimate, and might be wrong.

But with that said... some possible features:

  * dyn Trait
  * unit tests that can use ?
But, looking at the tracking sheet I linked below, it's looking like 1.26 and 1.29 are gonna be the biggies, some 1.27 stuff is pushed back a bit. We'll see!
>The big thing in this one is the LLVM upgrade, which has been a long time coming.

This is great news. I just ran my usual benchmark and it seems a performance regression has been fixed and some performance added over that. I'm measuring the speed of loading a Sony A77 raw file in rawloader[1]. That includes TIFF parsing and then a tight loop that processes every pixel. Here's the version history:

1.17 - 95ms

1.18 - 80ms (-16%)

1.19 - 79ms (-16%)

1.20 - 80ms (-16%)

1.21 - 79ms (-17%)

1.22 - 79ms (-17%)

1.23 - 79ms (-17%)

1.24 - 84ms (-12%)

1.25 - 74ms (-22%)

I believe the jump in 1.18 was LLVM 4.0 and now LLVM 6.0 is giving me a new performance jump. No idea what happened in 1.24 though but it seems to be fixed now.

[1] https://github.com/pedrocr/rawloader/

I'm out of the loop. How will "impl Trait" help with errors? Does it automatically improve errors, or just allow library authors to implement custom error or something?
Consider this code:

  fn foo_no(v: &[i32]) -> std::iter::Cloned<std::slice::Iter<i32>> {
      v.iter().cloned()
  }

  fn foo_impl<'a>(v: &'a [i32]) -> impl Iterator<Item=i32> + 'a {
      v.iter().cloned()
  }

  fn main() {
      let v = vec![1, 2, 3];
    
      let () = foo_no(&v);
      let () = foo_impl(&v);
  }
Given the let ()s, this will fail to compile for both. For the first, you get

  = note: expected type `std::iter::Cloned<std::slice::Iter<'_, i32>>`
for the second, you get

  = note: expected type `impl std::iter::Iterator`
The former gets longer and longer based on how many iterator chains you do, the latter will be the same no matter how many you add. It's also just generally way more clear, "I was expecting something implementing Iterator, and I didn't get it" vs "I was expecting this exact chain of nested iterators".
Ok. It looks like the new feature allows library authors to express their intentions with simpler code, and thus error messages will also be simpler. Looks good, thanks for taking the time to answer my question.
No problem! It also does enable some other things too. For example, closures have a type that's not writable, so

  fn foo() -> impl Fn(i32) -> i32 {
      |x| x + 1
  }
can be written, since closures implement the Fn trait, but there's no way to write the type directly.
Can you explain why an explicit lifetime is needed in foo_impl? I don't see a connection between the function argument lifetime and the return value lifetime, as the return value is a brand new object with cloned values... I realise I'm missing something fundamental here.
okay so, the un-elided version of foo_no is

  fn foo_no<'a>(v: &'a [i32]) -> std::iter::Cloned<std::slice::Iter<'a, i32>> {
I am not sure why the impl Trait version doesn't elide properly; I wrote it without the lifetime first and the compiler told me I need it, so I wrote it out, which is often what happens to me in these situations. I assume, since the elision rules were written years before impl Trait became a thing, that there's some sort of reason why it doesn't elide, but honestly, I'm not sure.
So why is the lifetime of the return value linked to the lifetime of the function argument? Couldn't the return value live longer than the function argument in that example?
In both cases, the iterators are over the slice, so their lifetimes need to be tied together. The iterator can't live longer than the slice, or it'd be a use after free. Does that make sense?
The Cloned iterator clones the elements of the underlying iterator, but only on-demand, so obviously it can't do that once the argument isn't live anymore.
Novice question: It says one step closer to support AVR, am I correct to assume the arduino is part of that family?

Because if so I have an amazing excuse to finally learn rust

If you want to get something done with microcontrollers, ARM Cortex-M tends to be much more powerful and is already great fun to write Rust for. It's a bit of work to get started--certainly a lot compared to Arduino which abstracts away even things that shouldn't be--but the code can be much more readable and portable than the corresponding C.

For example, get this $2 board http://wiki.stm32duino.com/index.php?title=Blue_Pill and use this library (see the examples!) https://github.com/japaric/stm32f103xx-hal

There probably will eventually be AVR implementations of https://github.com/japaric/embedded-hal so you could run mostly the same code on an Arduino board.

> Nested import groups

man, they work really hard to keep their language ugly & confusing

(comment deleted)
Nested import groups will often be uglier, but there are definitely some cases where they lead to much prettier code with no loss of obviousness, and nesting was a logical extension (in a “why don’t we have this?” kind of way, in my opinion) of import grouping, making that feature more internally consistent.
Does async/await have a slated release version yet? Also, is there a central resource to track progress on that topic?
There is a lot of activity around async/await. My understanding is that the hard work is nearly done, and what is left is mostly around the final syntax. I think its planned the land in stable with the Rust 2018 edition (https://blog.rust-lang.org/2018/03/12/roadmap.html) or shortly after it.

There is a recent blog series tracking the quite exciting progress being made at the moment (part vi, latest in the series): https://boats.gitlab.io/blog/post/2018-03-20-async-vi/

There is an experimental RFC tracking generators with an initial syntax design: https://github.com/rust-lang/rfcs/blob/master/text/2033-expe...

There's sorta two possibilities here; the procedural macro based version and some sort of native keyword version. We don't have a target release number yet, but sometime in the first three quarters of the year is a vague sort of target.

The networking WG just started literally today, and will be shepherding the work. I'm not yet sure where the best tracking issue is; if I find out soon I'll leave another comment.

For whatever my two cents are worth, I would choose a macro over a keyword every day. Keyword clutter can be incredibly stifling once a language has any history at all.
This llvm should have newgvn which allows better auto vectorization. I'm curious to see if panic=abort allows some of the nbody implementations at benchmark game to autovec. Might not have to wait on SIMD to beat c++. The fastest nobody entry is FORTRAN and it has no intrinsics, just autovec.
Does the LLVM version jump improve performance or doesn't really matter in that area?
As always, it depends on the exact code. I'm sure some stuff got faster, and some got slower; it really depends on the exact code being compiled.
> cargo doc, ... . It’s getting a huge speed boost in this release, as now, it uses cargo check, rather than a full cargo build

This is great. I often run cargo doc after adding new library dependencies or even for my own new interfaces to be able to click through the code. This is a wonderful tool, especially while IDEs are still coming up to speed.

Are IDEs using cargo doc? or are they reimplementing the markdown processing?

My understanding is that they use their own markdown processing, but I'm not actually sure. The RLS presents the raw text as far as I know; I'm not sure how the IntelliJ stuff works.
Instead to improve the "use" they had to remove it and enable only namespace renaming