Rust 2018 is here, but what is it?
hacks.mozilla.org
hacks.mozilla.org
Glad Rust learned from breaking changes hiccups of other programming languages :)
EDIT see discussion with kibwen below: fixed forward compat to backwards.
That's backwards compatibility: new versions of the compiler will continue to be compatible with code that was written previously. Forwards compatibility would mean that new code would be compatible with old versions of the compiler (this would be analogous to a website from 2018 working in IE5, or being able to play The Last Of Us on PS1). Rust's backwards-compatibility promise ensures the former, but not the latter.
That said, if you stick edition=2015 in your crates, that might go a liiiiitle bit towards keeping your code compiling on old versions of rustc, allowing you to partially "opt-in" to forward compatibility for your users, if you so chose. Though this would only keep you from accidentally using things that are edition-specific; remember that when you use a version of rustc from here on out, if you compile with the 2015 edition, you're not just getting a version of the compiler frozen as of last month; code compiling with the 2015 edition will still be receiving most of the same features as the 2018 edition (a strict subset, obviously; they're still the same compiler under the hood, only the frontmost of the frontend diverges).
• There are no disruptive changes. You'll just opt in to a couple of new keywords and some warnings turned into errors (most of which will be fixed for you automatically). The "old" code continues to work unchanged and can be mixed with the "new".
• In the last year, in general, Rust has fixed a bunch of papercuts in the syntax, modules/namespaces, and the borrow checker that were annoying and unintuitive. New tooling has been added. It's now easier for new users, so if you've hit a wall with Rust 2015, give Rust 2018 a try.
• Rust is finding its strengths in embedded, WASM, CLI apps, and these areas are getting more polished.
• High-performance networking is another area of focus, and async/await is coming soon.
Oh, and also this obscure little program gofmt ;-)
It's a basic IxD lesson about software tooling that I think more languages should pick up on.
Personally, I think I miss gofmt-on-save more than any other feature when I write other languages (gofmt is a lot more robust than autoformatters in most other languages). Being able to type carelessly and quickly and know the output will be corrected as soon as I hit CTRL+S lets me focus on putting my thoughts on the screen. And it even works as a soft-compiler: if the autoformatter breaks it means I have a bug somewhere.
My team works in equal parts go, ruby and javascript. Code review for go code is SO much easier. And it's mostly thanks to gofmt. It still amazes me how fervently some will argue about a single line of white-space or an optional semi-colon...
(But of course there are downsides, because if you're in a context where the editor/viewer isn't soft wrapping, it's worse. Still, on balance, I think not manually wrapping lines is the way to go.)
That said, no experience with these languages, so maybe I'm way off base. I'd be curious to see code samples, if you really feel like digging deep. :-)
Besides NLL, I really like the changes in the module system. Having to use extern crate was a drag, so it's nice that that isn't necessary anymore. Importing macros with use is both nicer and more intuitive than macro_use.
Very well done Rust team!
What do people usually do in these cases? Surely I'm not alone.
So I simply open a new tmux pane and run a file watcher (`entr`) which runs `cargo check` (and in other windows also clippy and test) and it works perfectly.
Since VSC has an excellent integrated terminal which you can split and have multiple tabs you could just do that in VSC itself. Frankly, I don't know any editor that displays live linting (and compiler feedback) in a way that I find useful but of course your YMMV.
I've only used Intellij IDEA, VSC and neovim though and I write JS (professionally) and now Rust and Haskell for fun.
Maybe one day you can shun the darkness and embrace emacs ;) all your problems disappear on that day
I'm not saying emacs and vim aren't really cool technologies. But why would a person who cares about optimizing keystrokes and crafting macros dismiss a tool that optimizes debugging and refactoring? I spend a heck of a lot more time staring at code than I do typing it, and if the reverse is true for you then you're some kind of savant and/or (more likely) you're writing too much code and reading too little of it.
Emacs takes a day to master? Awesome!
Integration has a lot of benefits like tell you the inference of intermediate types. "Don't care about good integration" isn't really advice.
It's like people who brag about syntax highlighting. The 99.9% rest of us consider it a good tool that improves our workflow.
Your options are to wait for it to improve, fix it yourself, or change your tools/workflow.
Some people seem to have had an allergic reaction to my comment. Maybe it's the emacs mention, which was tongue in cheek. Oh well ¯\_(ツ)_/¯ I'll leave it as it is... for this is a righteous battle!
You mentioned Emacs as a solution which has a Rust plugin that has problems like most other Rust tooling. Yes, Rust's tooling landscape is immature and still a work in progress.
Obviously you can just forgo editor integration all together. But you can do that in any editor.
All in all IDE support is a work-in-progress for Rust. It's definitely a lot better than 2 years ago, but still has a long way to go.
It's currently maybe the only thing which isn't that great regarding tooling in Rust. Other features, like cargo and crates.io, feel however miles ahead of C++.
C++ with conan is pretty nice, so it really depends on how you approach a C++ project.
However a thing that needs to be considered is that building good IDE support for Rust is a very hard task, since it's a pretty complicated language (e.g. with all the type interference, traits, automatic dereferencing and conversions going on).
Another fact is that the rustc compiler hasn't been built for supporting these kinds of use-cases right from the start, as e.g. the typescript and kotlin compilers have in my understanding. That means the IDE tooling can't directly query the compiler, but must duplicate some of the parsing and interference logic.
What is interesting is that Roslyn was a complete rewrite compared to the original C# compiler (which was written in C++ and couldn't power an IDE, so the tools like Resharper had to implement their own compiler front-end, essentially). IIRC, the ability to evolve the language was also a factor.
It seems that as the language matures, there comes a point where tooling (and further evolvability!) becomes important enough to justify the huge undertaking of rewriting the entire compiler. I wonder when will Rust reach that phase...
A huge issue however is still fragmentation. Only a minimal amount of libraries are provided and can be built in this fashion, and translating and maintaining build files of others is no fun.
Another annoyance can be getting into existing projects, where maintainers don't want to learn or invest in anything new, and claim that Makefiles and copying dependencies into subdirectories are just fine.
There's a huge difference between a state of the art modern C++ project and the typical things one encounters.
It is great that the team has achieved the Rust 2018 milestone, and there are already a few key companies adopting it, but it needs to catch up with 30 years of tooling, e.g. Qt, C++ Builder, CUDA, ...
Autocomplete works well. Macro support is improving all the time, and is pretty solid. Performance needs more work, but is generally acceptable.
I might have to try and do a proof of concept with one of our devices now :)
- it might be a easier language to target with a compiler because it's a higher-level intermediate language
- there would be fewer undefined behavior shenanigans
- successfully writing a compiler that targets Rust without using unsafe (too much) would mean getting (some of) the safety benefits as a side-effect. Could especially be relevant for languages aiming to introduce zero-overhead safety to the embedded environment themselves
- regarding previous point: discovering bugs in the process of porting to Rust
- because Rust plays relatively nicely with C and C++, one wouldn't have to lose existing libraries along the way
Porting a compiler could still be useful for the sake of legacy code though.
It allows learning from past mistakes, without being bound to supporting those past mistakes forever...
The module system improvements are so nice. Same with all the other ergonomic changes. Maybe I would feel differently about Rust if I had to use it professionally, but as a hobbyist, it's such a joy.
> rustup component add rustfmt
error: toolchain 'stable-x86_64-apple-darwin' does not contain component 'rustfmt' for target 'x86_64-apple-darwin' $ rustup component add rustfmt
error: toolchain 'stable-x86_64-pc-windows-msvc' does not contain component 'rustfmt' for target 'x86_64-pc-windows-msvc'
I looked for how to uninstall rust, and found that "the book" says that the command 'rustup self uninstall' will uninstall both rust and rustup. So I figured, what the heck, I'll start from scratch. I ran this command, uninstalling everything. Then I re-downloaded rustup-init.exe, and then ran it, reinstalling rust. I even then did 'rustup self update' and 'rustup update' for good measure.I still get the same error when trying to install rustfmt. What else is there to do if completely uninstalling and reinstalling doesn't work?
Thanks!
I'm still seeing the same behavior, even after following these directions.
> rustup self update
info: checking for self-updates
> rustup --version
rustup 1.15.0 (f0a3d9a84 2018-11-27)
Thanks for looking into this, though :)One small note on WASM, I really hope that wasm32-unknown-emscripten doesn't become an orphaned target. I've got quite a few WASM/ASM.js projects that have C dependencies which work great with WASM. Unfortunately wasm-bindgen and the like appear to require wasm32-unknown-unknown and since the cc crate doesn't work with wasm32-unknown-unknown I can't take advantage of them.
Totally love the wasm32-unknown-unknown target from a easy bootstrapping standpoint but it feels like that and the traditional target are starting to diverge pretty significantly(you can't do a cdynlib crate on wasm32-unknown-emscripten for instance).
Is there any plans in the works to bring the cc crate over to wasm32-unknown-unknown? Most of the impressive stuff I've been able to do with WASM has been pairing existing C libraries with Rust in the right ways and it would be unfortunate to lose access to that whole ecosystem.
"Rust has quickly become the best language for integrating with existing web applications."
Is that really true or some PR hyperbole?
In what senses? Interested to know, being both a CLI and a web dev guy, and not having looked much into either Rust or wasm yet (read a little of the Rust book, only, recently).
Second, in services. Microservice architecture means you can write parts of your applications in different languages, and we have really nice JSON handling, or whatever other format you’re using. People have moved high load services to rust and seen big gains.
Of course, there’s still lots of work to do, and there’s always compelling reasons to use other technologies too.
>we have really nice JSON handling, or whatever other format you’re using.
Good to know. Data formats and conversion between them (often, though not solely, in the context of CLI apps) is one of my interests and areas of experience, so will check out those aspects of Rust over time.
>People have moved high load services to rust and seen big gains.
Cool ...
Lots of things are using it as well https://crates.io/crates/serde/reverse_dependencies
Out of curiosity, what is missing for you? I'd imagine int generics and const fn, simd. What else?
Better GPGPU support, for CUDA and OpenCL, ideally with direct compilation of Rust code to GPU assembly.
The prime example is that code for working with modules that works fine on 2015 throws an error in 2018. This means that I now have to know two languages that are basically the same but have important differences. This is a real issue because I'm sure that some crates will jump to 2018 while others want to continue compiling on old compilers.
(EDIT: I now realize you're talking about crates that will continue development on Rust 2015 specifically because they want to target older compilers, which is a different concern from what I was addressing. But now I'm not sure what the concern actually is. Even if you stay on Rust 2015, what reason is there to not upgrade your compiler? Both editions remain supported in 1.31 and will be for...ever, as far as I know.)
There isn't a reason not to upgrade the compiler. My concern is that if I stay with Rust 2015 nothing changes for me, but if I need to fix a bug in someone's library there is a good chance they'll be on Rust 2018 (or vice versa). My concern is with the mental aspect of needing to remember the differences, not the technical differences.
It's possible that there isn't much to remember. Admittedly, I haven't yet looked at what the module changes are; but it still bothers me that such basic code no longer works.
It might not be a big deal, but a few weeks ago I started a new project that was going to reuse code from an existing project. The new project defaulted to 2018 (nightly compiler) and I got a bunch of errors with my module imports. It was just frustrating because it felt like it was a breaking change and one of the reasons I started using Rust is the promise of no breaking changes.
These kinds of changes are generally on par with other “never break” languages like Java and C++. Java 1.4 feels very different than Java 10.
It’s tough!
I fail to see how Rust editions could be much smoother managed. Or should they never fix anything in the language?
I'm really happy about the ability to mix older Rust 2015 and newer Rust 2018 code in the same project, and the tools for automatically migrating code to the 2018 edition. It took us like 3 hours to migrate a large code base at work, and I think I needed to file a bug report about exactly one upstream library. Everything else was seamless. And we can still use all our Rust 2015 dependencies.
For instance, previously I could `rustc src/main.rs -L deps` and assuming that the dependencies had all been built and had their rlibs put in deps things would just work. Now that is no longer possible, I have to manually specify every external dependency on the command line.
Official Rust blog post: https://blog.rust-lang.org/2018/12/06/Rust-1.31-and-rust-201...
(may be inconsistently available for a bit, as they seem to be in the process of updating the site to a new look)
* Universal pattern-matching (everywhere)
* Obvious syntactic sugar for defining and applying curried
functions, partial application.
* Type-classes (as a one single major innovation in PL
since Smalltalk)
* receive (or select) with pattern matching (Erlang, protobuf).
* first class typed channels (Go).
* multimethods (CLOS).
Thank you!Where are you missing pattern matching? The only place I can think of where it can't be used currently is the function signature. Might be hard to retrofit to the current syntax.
> Type-classes
Rust's traits are type-classes.
> first class typed channels
Why do they need to be first class? Unlike Go, Rust's type system is powerful enough that type-safe channels can be (and have been) implemented as a library. See crossbeam-channel.
fun map f [] = []
| map f (x::xs) = (f x) :: map f xs;
And boy, no TCO. What a shame.There is TCO, just not guaranteed. We’d like it; we have even reserved a keyword. Needs an RFC though.
And the functor typeclass in Rust looks like...?
My understanding from reading the article is that they have broken backwards compatibility; things using the new keywords that were introduced (e.g., "await") will no longer compile.
I think part of the confusion here is that there are essentially two things: there's the compiler, and there is the language itself. It appears (from the article) the compiler can presently support compiling either "edition 2015" or "edition 2018" of the language. (So, it's not the compiler's major we're bumping, rather, it's the language's.)
That flag which says which "edition" to use could just as easily specify which SemVer major version to use. So, instead of saying "use Rust edition 2015" or "edition 2018" it would just be "Rust (the language) v1" or "Rust (the language) v2". There's no difference aside from the naming.
The year doesn't particularly add anything, and obscures if we're jumping over multiple "barriers" of breaking changes or not. I'm not actually sure that that really matters.
My only thought is that having completely different looking things might be better in that it just visually distinguishes the two better. I've repeatedly seen devs muddle the difference between the language, and a implementation of the language, and what the language guarantees and what the implementation happens to do.
That's not backwards compatibility, it's forwards compatibility. Whenever any language adds a new feature, using that feature breaks the build on old compilers. Setting `edition=2018` is no different. But the important thing is that old code continues to build unmodified on new compilers. That's what the edition system is preserving.
This is not the case.
That the compiler has the user opt-in to the never version by setting a flag is irrelevant, for the discussion of versioning. (Now, for an end-user, I think it's great: you do not want to automatically break working code. I'm not saying the flag shouldn't exist, I'm saying the flag exists because the change is breaking.)
> All existing code compiles and will continue to compile with no changes into the future.
My point, again, is that it won't compile without changes in the new edition. "Edition" is the word that was chosen (and it's not necessarily a bad word), but what it is is a version for the language. Whenever a backwards incompatible change (or set of changes) is going to be made to the language, that's a new "edition", but you could just say "version" and it would be as accurate.
Again, consider the language and compiler as separate entities, each with their own API, and I think it becomes clearer. Changes have been made to the language (e.g., the addition of new keywords) that render strings previously valid in the language now invalid, or valid with a changed meaning. In any system, this is a breaking change. This change could, were SemVer used, be identified with a major version bump.¹
That the compiler (which itself has a version number, separate from the language now) is capable of recognizing a flag and switching internally which version of the grammar it uses is great, but again, proof that there exists a version of the grammar (called "edition").
¹But honestly, that ship has sailed for Rust. And that's fine, and I think it's just a different term, and that it has a good chance of helping better convey the meaning by simply being different. But the argument that, under that, it's essentially SemVer, is nonetheless true.
(That at present the compiler and the language are very much intermixed makes this much more muddled. Were the language's definition more formal, I think this would all get much clearer, as we could ignore rustc, and talk about the language.)
Would anyone like to share other examples of this sort of thing (such as in other compilers or tools)?
In C, enums are more like:
#[repr(C)]
enum Foo {
BAR=0,
BAZ=1,
Unknown(i32), // other possible values of int
};
but Rust doesn't allow to write enums like that, so it leads to bugs in FFI.JSC also uses them extensively in the lexer and parser.
#[repr(C)]
#[derive(PartialEq, Eq, Clone, Copy)]
struct Foo(i32);
const FOO_BAR: Foo = Foo(0);
const FOO_BAZ: Foo = Foo(1);
I remember writing a macro to handle this properly, even using modules for namespacing, ages ago... #[repr(C)]
#[derive(PartialEq, Eq, Clone, Copy)]
struct Foo(i32);
impl Foo {
const BAR: Foo = Foo(0);
const BAZ: Foo = Foo(1);
}My solution to this problem, for i8/u8 enum's only, is just to list of unused values as enum Foo { ..., RESERVED_2=2, RESERVED_3=3,.. RESERVED_255=255,}.
cenum! {
Foo: i32,
Bar = 1,
Baz = 2
}
That expanded to the above `struct Foo(i32)` code and implemented Debug printing `Foo::Bar`, `Foo::Baz`, and `Foo::Unknown(3)` or something.From a glance, I would imagine you could mediate across the FFI using an `i32`, and provide an `impl From<i32> for Foo` that neatly separates the discriminated and excess cases.
I saw number of tickets about that in Rust ticketing system. This one seems to be most relevant:
https://github.com/nikomatsakis/rust-memory-model/issues/41
Then these ones:
https://github.com/rust-lang/rust/issues/36927
https://internals.rust-lang.org/t/c-style-enums-in-ffi-and-a...
https://internals.rust-lang.org/t/pre-rfc-enum-from-integer/...
> This behaviour affects Prost: i32 type is used instead of enum type, because Protobuf defines that enum variable must be able to hold values outside of enum range to be compatible with future versions. As alternative solution, it's proposed to use _Unknonwn_(i32) variant, but this solution cannot be implemented in current version of Rust, because C-like enums cannot contain tags.
In principle, a protobufs library (like Prost) could map a protobuf enum `X` into a Rust enum `ProtobufEnum<X>` with `Known(X)` and `Unknown(i32)` fields. Or autogenerate a Rust enum with the known variants inlined, plus the extra Unknown field.
It is incumbent on the library author and/or FFI user to map semantic notions appropriately across the interface. Rust enums do not have C semantics, even if the underlying machine representation is annotated to mimic C. If something across the interface allows its flavor of enum to take on unnamed values, then that semantics needs to be preserved faithfully in Rust -- and that may mean using something slightly different from what Rust itself natively provides.
Unfortunately, support for this extra Unknown() field is not added to rustc yet even in Rust2018 edition. Compiler can do that, because it already used to save space for Option<>.
> Rust enums do not have C semantics, even if the underlying machine representation is annotated to mimic C.
Yes, it's the bug I'm talking about. Solutions are: a) remove repr(C), b) fix repr(C).
For those very rare cases where someone used open-ended enums in their API, ascribing some meaning to unnamed values that must be exposed, you can always use i32 etc.