The most exciting component of this release is the const fn improvements. With loops and if available, you can now do non-trivial computation in const fn for the first time. It reduces the gap between Rust's const fn and C++'s constexpr to a large degree. Ultimately, the miri execution engine that this feature builds upon, supports a much larger set of features that even constexpr supports. It's been a project spanning years to get it merged into the compiler, used for const fn evaluation, and stabilize it's features (this part is far from over).
Previously, the table was present as an array literal in C-style, now I can remove it once I decide for the library to require the 1.46 compiler or later versions.
I’m learning rust right now and there is a lot to like. Steady updates like this are also very motivating. The ecosystem feels very sane - especially compared to npm. Top notch Wasm support, cross compiling is a breeze.
That said, coming from a FP background (mostly Haskell/JS, now TS) Rust is... hard. I do understand the basic rules of the borrow checker, I do conceptually understand lifetimes, but actually using them is tricky.
Especially in a combinator world with lots of higher order functions/closures it’s often completely unclear who should own what. It often feels my library/dsl code needs to make ownerships decisions that actually depend on the usage.
Anyways, I guess this gets easier over time, right? Should I avoid using closures all over the place? Should my code look more like C and less like Haskell?
[edit] great answers all, providing useful context, thanks
> Anyways, I guess this gets easier over time, right?
Yes.
> Should I avoid using closures all over the place?
Not necessarily.
> Should my code look more like C and less like Haskell?
Yes. Others sometimes don't like to hear this, but IMO, Rust is not at all functional. Passing functions around is not ergonomic (how many function types does Rust have again? Three?). Even making heavy use of Traits, especially generic ones, is difficult.
Rust is very much procedural. Java-style OOP doesn't work because of the borrowing/ownership. And FP style function composition doesn't work without Boxing everything. But then you'd need to be careful about reference cycles.
>the borrow checker, I do conceptually understand lifetimes, but actually using them is tricky.
I've been using Rust for a little over year, almost daily at work, and for several projects. I have a pretty good intuition about how the borrow checker works and what needs to be done to appease it. That said, I don't think I'm any closer to understanding lifetimes. I know conceptually how they are supposed to work (I need the reference to X to last as long as Y), but anytime I think I have a situation that could be made better with lifetimes, I can't seem to get the compiler to understand what I'm trying to do. On top of that very little of my code, and the code I read actually uses lifetimes.
Another edit: I am not a Functional programmer, and have never known Haskell or any Lisp. Erlang is as close as I've ever gotten. I've found Rust to be a fantastic language for writing Functionally.
Ah, if you’re making dsl code or functional combinators, you usually want to ‘move’ your values instead of ‘borrowing’ them.
example:
fn add(mut self) -> Self { self }
fn add(self) -> Self { self }
instead of:
fn add(&mut self) {}
fn add(&self) {}
With this, you will be able to ‘store’ closures easily and apply them later. No more fighting with the borrow checker over where to borrow as mut or not. You will also avoid a few copies.
Sometimes you will be annoyed by changes I guarantee it BUT since 1.0 that's decreased a lot and compared to npm it's night and day. You'll think you're dealing with C in relative terms of stability if npm is your baseline :D
Meta-answer: my default when picking up a new language is usually to learn just enough to be able to start writing code, and then learn new things piecemeal as necessary to solve whatever thing I'm working on, and it sounds like you're hoping to do something like that here.
I found that approach for Rust in particular to not work well at all, and have colleagues who've reported the same. There are some fairly complicated, fundamental concepts that are unique to Rust that I think need to be tackled before you can really do much of anything (mostly borrowing and lifetimes), and that's not immediately obvious from starter programs -- because of lifetime elision, some early programs can look deceptively familiar, but there's a bunch of barely-hidden complexity there, and as soon as you start to stray from the tutorial path, you'll run headfirst into a wall of compiler errors that you're not yet equipped to understand. For Rust I'd highly recommend just reading a book cover to cover first (either TRPL or the O'Reilly one), and then starting to write code.
One thing I'd be wary of is Googling error messages and taking answers from Stack Exchange. Rust has mutated (heh) a fair bit over the years and many SE answers to noob problems are obsolete and sometimes incorrect. At the very least check the datestamp on any answer and be wary of anything more than a year or two old. This goes double if the answer has some extremely awkward looking syntax with lots of modifiers and underscores sprinkled throughout. There's probably a better way to do it now, or an alternative solution that works better. Or maybe you're just trying to do something that Rust makes hard, like concurrent processing on a shared data structure.
The manual is safer even though it's harder to find your exact problem and solution, especially when you're just starting out.
Trying to implement anything in Rust will set you up for a crash-course. Even the simplest non-trivial programs will introduce you to the Rust borrow checker, a major feature absent in C# / Python.
Once you've learned the basics (plenty of links in the siblings, including the official Rust Book), this is a key (and entertaining!) unofficial resource that really hammered home for me the ways that Rust is different from the C family when it comes to working with references: https://rust-unofficial.github.io/too-many-lists/
It also taught me about Boxes and Rc's, which are essential for certain kinds of things, and which I don't remember being covered in the main Rust Book at all
You might check out https://exercism.io/tracks/rust . Some are a little heavy in the math department but personally I've always found test drive learning useful when learning a new language thanks to instant feedback.
I am in similar boat. Python centric data scientist. Very tempted to try to learn Rust so I can accelerate certain ETL tasks.
Question for Rust experts: On what ETL tasks would you expect Rust to outperform Numpy, Numba, and Cython? What are the characteristics of a workload that sees order-of-magnitude speed ups from switching to Rust?
I like to implement something I have already done before. In my case, the ID3 algorithm has a nice balance of challenge, experience and documentation available. You could try to write it for a specific case, where you structure your data, and then apply it to a generic case.
Try to do some graphics programing with thr backend of your choice. There is also this cool nanovg port https://github.com/cytecbg/gpucanvas. Run the demo in examples to see the nanovg demo.
It is the Rust way of specifying a function as being _pure_.
In other words the output is dependent only on the function arguments, and not on any external state.
This means they can be evaluated at compile time.
I suppose in the future, it could also allow better compiler optimizations.
Glad to see `Option::zip` stabilized. I tend to write such a helper in many of my projects to collect optional variables together when they're coupled. Really improves the ergonomics doing more railroad-style programming.
The quality of life improvements to cargo look very nice, and I feel that rust wouldn't be remotely as successful without such a tool. I'm very glad I won't have to be manually picking target directories out of my borg backups anymore when I'm running out of disk space.
Yeah, but only if you use them to compute significant items during compilation.
The upside of course is that any computation you compute at compile time is a computation that you don't compute at runtime. For some applications this trade off is definitely worth the cost of admission.
At the end of the day it's a trade off that will have to be made in light of the scenario it's being used in. Being able to make that decision is a good thing.
Awesome! Any idea when relative links will be available in rustdoc? Seems like it's just on the edge of stabilizing (https://github.com/rust-lang/rust/pull/74430) but I'm curious how long it takes to see in a release after this happens.
There can be fuzziness here depending on exactly when it lands, but generally, if something lands in nightly, it'll be in stable two releases after the current stable.
Common Lisp user here. Why just that? How come you can’t have the entire language as well as all your language customizations available at compile time for evaluation?
You can! Just not through `const fn`. Rust has macros, which at their limit are capable of running arbitrary Rust code that manipulates syntax and communicates with the compilation process, just like in a good old Lisp.
Why isn’t `const fn` like this too? One word answer: determinism.
Rust takes type/memory/access/temporal safety very seriously, and consequentially you can’t use anything in a `const fn` that isn’t fully deterministic and doesn’t depend in any way on platform-specific behavior. This includes, for example, any floating-point computation, or any random number generation, or any form of I/O, or handling file paths in an OS-specific way, or certain kinds of memory allocation. The span of things possible in `const fn`s has been expanding over time, and will in the nearish future largely overtake C++’s direct counterpart of it (`constexpr` functions) in capability. But some things will intentionally never be possible in `const fn`s, for the reasons given above.
Can Mozilla layoffs (on Servo team) impact Rust future?
It is just s question to understand if there are others big rust project healty out of there. Just curious
> entire rust team was recently fired from Mozilla
This is completely incorrect, verging on FUD. Mozilla had very few people working full-time on Rust; of the people who were laid off in the recent wave, the ones working on projects adjacent to Rust were working on Servo or WASM-related codebases. In particular, the person at Mozilla who was most central to Rust development, Niko Matsakis, is still employed there and still working full-time on Rust.
They're moving to a Rust Foundation with corporate sponsorship model. I think Rust will be fine, even Amazon expressed interest in sponsoring development.
est31|5 years ago
In addition to the linked examples, I have some code of my own which is made simpler due to this feature: https://github.com/RustAudio/ogg/commit/b79d65dced32342a5f93...
Previously, the table was present as an array literal in C-style, now I can remove it once I decide for the library to require the 1.46 compiler or later versions.
Link to the old/current generation code: https://github.com/RustAudio/ogg/blob/master/examples/crc32-...
crazypython|5 years ago
tines|5 years ago
This sounds promising. Can you give examples? I don't know Rust at all, and the reason I like C++ is its metaprogrammability.
marricks|5 years ago
> All boolean operators except for && and || which are banned since they are short-circuiting.
I guess I'm missing something obvious but why does the short circuiting break const-ness?
sfvisser|5 years ago
That said, coming from a FP background (mostly Haskell/JS, now TS) Rust is... hard. I do understand the basic rules of the borrow checker, I do conceptually understand lifetimes, but actually using them is tricky.
Especially in a combinator world with lots of higher order functions/closures it’s often completely unclear who should own what. It often feels my library/dsl code needs to make ownerships decisions that actually depend on the usage.
Anyways, I guess this gets easier over time, right? Should I avoid using closures all over the place? Should my code look more like C and less like Haskell?
[edit] great answers all, providing useful context, thanks
ragnese|5 years ago
Yes.
> Should I avoid using closures all over the place?
Not necessarily.
> Should my code look more like C and less like Haskell?
Yes. Others sometimes don't like to hear this, but IMO, Rust is not at all functional. Passing functions around is not ergonomic (how many function types does Rust have again? Three?). Even making heavy use of Traits, especially generic ones, is difficult.
Rust is very much procedural. Java-style OOP doesn't work because of the borrowing/ownership. And FP style function composition doesn't work without Boxing everything. But then you'd need to be careful about reference cycles.
nemothekid|5 years ago
I've been using Rust for a little over year, almost daily at work, and for several projects. I have a pretty good intuition about how the borrow checker works and what needs to be done to appease it. That said, I don't think I'm any closer to understanding lifetimes. I know conceptually how they are supposed to work (I need the reference to X to last as long as Y), but anytime I think I have a situation that could be made better with lifetimes, I can't seem to get the compiler to understand what I'm trying to do. On top of that very little of my code, and the code I read actually uses lifetimes.
thenewwazoo|5 years ago
* If you need to keep unchanged the input, you must either use a reference-to (.iter()) or copy-of (.iter().cloned()) of each item
* If you don't need the input ever again, you should move the items (.into_iter())
These rules follow for each step of the chain.
I very very often write very Functional code in Rust and I find it natural and easier to reason about than imperative-style code. The example I could find the fastest: https://github.com/thenewwazoo/aoc2019/blob/master/src/day10...
Edit: another example (this one uses types that are Copy so the copies are implicit) https://github.com/thenewwazoo/cryptopals/blob/master/src/tr...
Another edit: I am not a Functional programmer, and have never known Haskell or any Lisp. Erlang is as close as I've ever gotten. I've found Rust to be a fantastic language for writing Functionally.
vmchale|5 years ago
It ends up being doable. I dabbled in ATS, developed Stockholm syndrome, and now Rust ain't too bad.
Higher-order functions are difficult in Rust or with linear/affine types in general. Haven't looked at what Rust does recently.
> Should I avoid using closures all over the place? Should my code look more like C and less like Haskell?
When in Rome do as the Romans :)
Anyway, some fun imperative programming stuff you can do in Rust that is fickle in Haskell (or OCaml/Standard ML).
Fiahil|5 years ago
example:
fn add(mut self) -> Self { self }
fn add(self) -> Self { self }
instead of:
fn add(&mut self) {}
fn add(&self) {}
With this, you will be able to ‘store’ closures easily and apply them later. No more fighting with the borrow checker over where to borrow as mut or not. You will also avoid a few copies.
stevencorona|5 years ago
There is a lot to like, understand lifetimes conceptually, but it's hard.
Shorel|5 years ago
It is definitely not easier compared to C++, contrasting with D, which is easier than C++.
However, the program worked correctly at the first try, which I guess it is also a consequence of the Rust model.
zozbot234|5 years ago
Now that's damning with faint praise.
unknown|5 years ago
[deleted]
stjohnswarts|5 years ago
kumarvvr|5 years ago
Are there any particular set of problems that I can solve systematically, so that I can learn all the features of Rust?
steveklabnik|5 years ago
https://doc.rust-lang.org/stable/rust-by-example/ is the "by example" introduction, which is all about sample programs, but feels a bit dated, IMHO. Still not incorrect, but not up-to-date.
You may also like the O'Reilly book, or Rust In Action, which use more fully-featured example programs more heavily than The Book does.
apendleton|5 years ago
I found that approach for Rust in particular to not work well at all, and have colleagues who've reported the same. There are some fairly complicated, fundamental concepts that are unique to Rust that I think need to be tackled before you can really do much of anything (mostly borrowing and lifetimes), and that's not immediately obvious from starter programs -- because of lifetime elision, some early programs can look deceptively familiar, but there's a bunch of barely-hidden complexity there, and as soon as you start to stray from the tutorial path, you'll run headfirst into a wall of compiler errors that you're not yet equipped to understand. For Rust I'd highly recommend just reading a book cover to cover first (either TRPL or the O'Reilly one), and then starting to write code.
jandrese|5 years ago
The manual is safer even though it's harder to find your exact problem and solution, especially when you're just starting out.
xvedejas|5 years ago
brundolf|5 years ago
It also taught me about Boxes and Rc's, which are essential for certain kinds of things, and which I don't remember being covered in the main Rust Book at all
BiosElement|5 years ago
shawnz|5 years ago
datanecdote|5 years ago
Question for Rust experts: On what ETL tasks would you expect Rust to outperform Numpy, Numba, and Cython? What are the characteristics of a workload that sees order-of-magnitude speed ups from switching to Rust?
gchamonlive|5 years ago
adamnemecek|5 years ago
unknown|5 years ago
[deleted]
somurzakov|5 years ago
smabie|5 years ago
thomasahle|5 years ago
It is the Rust way of specifying a function as being _pure_. In other words the output is dependent only on the function arguments, and not on any external state.
This means they can be evaluated at compile time. I suppose in the future, it could also allow better compiler optimizations.
ko27|5 years ago
the_duke|5 years ago
const functions can't directly do any IO or even allocation - at the moment.
But this can be easily circumvented, eg by using a proc macro that does IO.
Sidenote: even in Haskell the function signature doesn't guarantee purity, due to unsafePerformIO.
pitterpatter|5 years ago
kevinastone|5 years ago
borgel|5 years ago
LEARAX|5 years ago
cordite|5 years ago
steveklabnik|5 years ago
One thing that people may not realize, especially now that we have loop. You may expect this to hang the compiler:
But it won't: This does place an upper limit on any given const fn.Verdex|5 years ago
The upside of course is that any computation you compute at compile time is a computation that you don't compute at runtime. For some applications this trade off is definitely worth the cost of admission.
At the end of the day it's a trade off that will have to be made in light of the scenario it's being used in. Being able to make that decision is a good thing.
orthecreedence|5 years ago
steveklabnik|5 years ago
fmakunbound|5 years ago
> while, while let, and loop
> the && and || operators
Common Lisp user here. Why just that? How come you can’t have the entire language as well as all your language customizations available at compile time for evaluation?
pthariensflame|5 years ago
Why isn’t `const fn` like this too? One word answer: determinism. Rust takes type/memory/access/temporal safety very seriously, and consequentially you can’t use anything in a `const fn` that isn’t fully deterministic and doesn’t depend in any way on platform-specific behavior. This includes, for example, any floating-point computation, or any random number generation, or any form of I/O, or handling file paths in an OS-specific way, or certain kinds of memory allocation. The span of things possible in `const fn`s has been expanding over time, and will in the nearish future largely overtake C++’s direct counterpart of it (`constexpr` functions) in capability. But some things will intentionally never be possible in `const fn`s, for the reasons given above.
djhworld|5 years ago
steveklabnik|5 years ago
daitangio|5 years ago
steveklabnik|5 years ago
echelon|5 years ago
I can't wait for when we'll be able to `const fn` all the things. Regex, expensive constants that feel as though they should be literals, etc.
scott31|5 years ago
[deleted]
kibwen|5 years ago
This is completely incorrect, verging on FUD. Mozilla had very few people working full-time on Rust; of the people who were laid off in the recent wave, the ones working on projects adjacent to Rust were working on Servo or WASM-related codebases. In particular, the person at Mozilla who was most central to Rust development, Niko Matsakis, is still employed there and still working full-time on Rust.
AsyncAwait|5 years ago
adamch|5 years ago