Is it really a good idea to target a release candidate of LLVM in the Stable branch by default? I can understand wanting to benefit from new features, but I wouldn't want production code to be running on something that LLVM does not yet consider ready for release.
Rust tends to exercise code paths in LLVM that clang doesn't, so from Rust's perspective LLVM was never particularly stable (e.g. pointer aliasing has been broken in LLVM for years). In that case it's better to get fixes for known bugs in LLVM ASAP.
It's a little thing, but one of my favourite parts of any Rust release is seeing which stdlib functions become `const`. The ergonomics of build-time evaluation are slowly improving, and I'm very excited to slowly do less at runtime!
can be, you can call them at runtime too. And to be clear, const generics aren't stable yet, they're being used as an implementation detail of the standard library here, you cannot yet use them on stable in your code.
Const values are inlined at compile time wherever they are used.
Const functions let you run code to define const values. This code will be run at compile time before the const values are inlined.
For example let's say you have a constant for PI but you need 2*PI. You could do the multiplication in const context, meaning the multiplication will happen during compilation and not execution. Slowly more and more of the language is becoming usable in const context so more advanced things can be done during compile time.
I like that const functions occupy a space between a normal function and hygienic macros. You can avoid writing out lookup tables long-form where the entries are very predictable. For instance you could write a base64 encoder and decoder without putting ["a", "b", "c"... ] in your code base, and then have to worry if you accidentally put "D" in there twice. The typical alternative in other languages is to evaluate this at startup time, which adds latency to your program.
Compile-time function evaluation is also one of the features of the Jai programming language. If you can't get people to use your language, getting them to steal all your features is a good consolation prize.
I think the point is that rustc + llvm has long made everything "constexpr" (well as much as it can). After all it's a missed optimization if it fails to evaluate something at compile time if it can be so evaluated.
`const fn` is merely an API guarantee that the function can always be evaluated at compile time if the arguments are themselves known at compile time.
In theory, yes. In practice, it is an API commitment, and so it is not inferred, because you as a library author have to decide if you want to commit to it or not.
Yes, this is already done. But that's not the more interesting part of the feature. Once you require something to be const, you can use it in contexts that require something to be const. Inferring it in non-const contexts is less powerful.
Can increase in use of `const fn`'s create a noticeable decrease in compile times? As someone whose never worked on compilers, I'm not really sure what the magnitude of that processing time is in relation to everything else.
Just how prevalent is Rust in the industry? I remember trying it out back in 2014 and saw nothing of interest, as a Haskell user. A lot of the main "selling points" of Rust are getting introduced in C++. Also, what is it about Rust that gets automatic top view on HN?
The fact that you can actually ship an app. I've been writing my app (it's an IDE for music http://ngrid.io) in Rust after trying it in cpp and swift and it's a joy to use.
Haskell is cool and all but every time I tried it, I spent too much tle with package management for example.
Any screenshots of the application your working on. I seriously love working on music and I'm down to try anything, but I have no idea what your app actually is
What is "ship an app" supposed to even mean here? Being able to build a binary for distribution? Both Swift and C++ can do that, and likely Haskell too.
I am a fan of OpenGL based GUIs. I built my first one before Y2k. There's something nice about crafting UI widgets, with all their behaviors that scratches a particular itch I can't explain.
That said, it is really really tough to get all the little behaviors that people rely on. Including all keyboard shortcuts (across all platforms), double/triple clicking words, etc.
I guess that this may not matter that much for specialized software (but please focus on the damn textboxes, they are hard to get right). I'm just commenting in case other people decide to follow the same path.
Also, for a broader audience: be mindful of accessibility. Usually, this kind of thing doesn't work with screen readers.
I think the language is nice however the niceness comes at the cost of performance. Swift has a relatively unique performance in that it's faster than garbage collected languages but slower than C or Rust due to some hidden runtime locks.
Another problem is with the package ecosystem. It feels like most of Swift ecosystem is wrappers around Apple APIs.
There are also some strange workflow edge cases like the fact that you currently cannot use Metal shaders in Swift Packages. This is due to the fact that Xcode projects are supposed to be ephemeral (not version controlled, but generated when you clone the package). However if you want to compile Metal, you have to add some compiler options. As a result, every time you add a dependency, you have to regenerate the XCode project file, you have to fuck with your XCode project to add the compiler options.
Ironically, you can use Metal in Rust crates just fine.
Rust is also truly cross-platform. Windows and Linux have first class support.
To summarize my argument, Swift feels like daddy Apple is telling me what is best for me which may or may not be true. Rust feels like true empowerment. If there is something that doesn't suit me for whatever reason, there is generally a way of working around it.
I guess it depends on what you are doing. Swift is OK for iOS/macOS development but other than that Rust is more advanced. Rust macros are really powerful, things that need to be added explicitly to the Swift compiler can be implemented as Rust macros.
> Swift has a relatively unique performance in that it's faster than garbage collected languages but slower than C or Rust due to some hidden runtime locks.
Programming languages can be a very personal thing. But I will offer my perspective having used C++ extensively (both modern and awful), Go, Rust, a little Haskell and some 20 other languages in my two decades career.
Rust is one of the nicest languages out there. Very well thought out, elegant, full low-level control for real systems programming (sorry Go!) AWS uses it a lot, Microsoft is starting to use it.
Go, or even Python is better if you just want to make a backend API for a (web) app. You'll be more productive with it over Rust because you have a GC and can ignore the whole ownership issue for the most part. Python for very small teams likely to stay small for a long time, Go otherwise.
C++ has come a long way, but it's still hairy and loaded with footguns and baggage. I would only use it these days if I needed to use a big C++ library (like a game engine).
I am no longer willing to use Haskell, PHP, Perl, Java or JVM languages, .NET or anything else that's not Go, Rust or Python. With the same caveat as C++ above. I pass over job postings that require them. That's not saying those languages are bad, just that in my personal opinion there are better tools out there, and if I'm going to work with it day in and day out, it had better be in something at the top of my list - because life is short.
Speed, developer ergonomics and community. It depends what you are building I guess but for example audio processing or graphics is still impossible in either of those.
Saying AWS uses it a lot is very misleading. Same for Microsoft. I won’t dispute that there are some teams using it but the majority of new projects aren’t in rust and there’s no trend for it.
Do you work at Amazon? That's not what it looks like from the outside. Lambda and Fargate are huge services, even if they're not all of AWS, and it only seems like they're ramping up more, even just from looking at the rate they're hiring Rust devs.
Yes, it is not super massive, but it is in the production path for important things, and appears to be on quite an upward trajectory.
It’s not a majority of course, but I think ‘a lot’ is a fair assessment. It is indeed ramping up. Earlier this year there were public job postings for an AWS Rust SDK. I’ve also seen adoption for it in critical components outside of Lambda/Fargate.
To be clear, I mean they're using it relatively more aggressively for new projects than other large enterprises. It's obviously early days and the vast majority of AWS services are implemented in other languages (probably java, but that's just an educated guess.)
Rust in 2014 and Rust today are very different; specifically, 2014 to 2015 was a very tumultuous time. 2015 saw the release of Rust 1.0, and so a lot of stuff happened to get things ready for that, including some very large changes to the entire standard library, a removal of most of the runtime... all sorts of stuff.
If you're a genuine Haskell user then it shouldn't take more than a second to realize that true borrow checking is not the same as C++ pointer types, and that Cargo is thee best dependency manager. Although, being a Haskell user, you might suffer stockholm syndrome in regards to the dependency hell that is cabal or stack.
Just think that it took C++ 40 years to reach where it is now and still there are domains where C rules, a systems language that people forget is only 10 years younger than COBOL.
It has been picking up a lot lately, Apple, Amazon, and Microsoft are all showing some adoption, for instance.
> I remember trying it out back in 2014 and saw nothing of interest, as a Haskell user
The interest for a Haskell user would be that you have some of the functional programming features you like (such as discriminated unions!) with C/C++ levels of runtime performance/efficiency. For someone who has no need for that level of low level control/performance, Rust is generally not a language you want to use, for sure.
> A lot of the main "selling points" of Rust are getting introduced in C++
It is true that you could, with sufficient coding standards and linters, get something approximately like Rust by using C++, where the linter disallows any non safe pointers, any use of null or nullable types without wrapping it in a variant, disallowing all cases of undefined behavior, and so on. However you would have something a lot less pleasant to use than using Rust in the first place, build times would probably end up worse with those thing enforced as a build step, and setting up such an environment would be non trivial and you basically are just building rust out of C++, twine and gum, so why not just use Rust?
Rust doesn’t support object inheritance (only traits). It also doesn’t have function overloads or default arguments. Qt relies heavily on all of these C++ features; inheritance is fundamental to how Qt works (for example, it is impossible to respond to mouse/keyboard events without subclassing to override virtual methods), and function overloads with default arguments are used everywhere. As a result, it’s a struggle to shoehorn this object model into Rust.
rust-qt[0] doesn’t support inheritance at all, so you can only connect to things in Qt which accept callbacks using slots. It requires `'static` lifetime on those callbacks, so as far as I can tell you are also forced to use `Rc`s whenever you are doing something with a rust-qt object even if you should be able to use a plain reference. The code is auto-generated, so all APIs are `unsafe`, and no function overloads means names are gross because the function signatures must be expressed in the function name (e.g. the `QMessageBox` constructor[1] becomes `QMessageBox::from_icon2_q_string_q_flags_standard_button_q_widget`). This could be improved by using `Option`s for arguments with defaults, but right now this is what it looks like.
qmetaobject-rs[2] has some support for inheritance but as far as I can tell it’s limited to a couple of base types only. It’s also designed around using QML, so it doesn’t actually expose most of the Qt API. As such I don’t have too much experience with it.
I’m unaware of any other usable Rust Qt bindings right now. I currently make things work by using rust-cpp[3] to create C++ subclasses and smuggle events, but this sucks because it also means the objects on the Rust side need to have the Qt API re-exposed manually since AFAIK there isn’t a way to have Rust handle that without using a code generator. (There might be some hackier ways to make this work, or maybe someone with more Rust experience than me knows of something smarter that could happen, but in any case the ergonomics right now are not good for the average developer.)
> I remember trying it out back in 2014 and saw nothing of interest
The 1.0 release was in 2015, and there wasn't much industry adoption before that. Today most of the big tech companies are using it for something. Microsoft published a series of articles this year about using Rust internally.
> A lot of the main "selling points" of Rust are getting introduced in C++.
In the C++ world, Rust's biggest selling point by far is memory safety. The safety story is based on notions of ownership, borrowing, and lifetimes that are built into language itself. Most of the standard library, and many foundational crates like Serde and Rayon, have designed their APIs around these concepts. These things tend to go "all the way down": Every single function or datatype that deals with references defines its lifetime requirements for those references, and every single callsite of one of those functions or variable of one of those types is statically checked to meet those requirements.
I wouldn't go so far as to call this an "all or nothing" thing. Languages like TypeScript make it clear that you can get good value out of a strict type system even when much of the ecosystem isn't strict. But there's a lot of value in being on the "all" side of the fence here -- especially for languages with pointers and destructors -- and I think it's unrealistic to expect an established language like C++ to make that many changes.
> Also, what is it about Rust that gets automatic top view on HN?
Having a new, serious contender in the C/C++ world is exciting! And maybe Rust being more challenging to learn makes people more excited about being "in the club" when they do learn it. On the other hand, all the talk about memory safety seems to make some people kind of...self-righteous?...about the language, which we would all prefer to see less of :p
I'm currently rewriting the "industry" application I started 4 years ago in Python into Rust. At the time I wasn't sure if Rust was mature enough to use it for a critical application, and I needed to get started quickly so Python seemed like the right choice.
Now that the application has grown to be quite huge I really struggle to maintain the python code, I miss static typing. Big code changes are a pain to validate and regressions always manage to slip in the cracks. Meanwhile I've used Rust for some smaller, less critical components and it Just Works. C-tier perfs and zero crashes in 4 years.
I'm definitely optimistic for the future of the language at this point. The community is thriving and the language keeps improving, while not breaking backward compatibility and forcing me to update my code to make use of the latest features.
I've found that religiously applying type annotations in Python helps, and using a mypy LSP plugin[1] allows for type checking as I'm writing Python code.
I've given it a try but I found it really half-baked. For one thing I don't really believe you can successfully tack a type system as an afterthought onto a dynamic language but beyond that the fact that the default Python implementation effectively completely ignores these annotations make them borderline useless for me. If at the very least I could generate a runtime error on type mismatch that'd be worth it.
As they stand I thought that they took a lot of effort to write and maintain for very little practical value.
I think contributing to the popularity here is that Rust is interesting to discuss out of an academic perspective due to the niche it has carved out, with the compiler able to see already at compile time whether your code is thread safe with no race conditions, and without memory leaks and buffer overruns. This without support like a garbage collector or a virtual machine overseeing things. OK, it's not bullet proof, but large swathes of issues are prevented that often aren't in other big languages, and you kind of have to go out of your way to shoot yourself in the foot.
As for use in the industry, I see it mentioned every now and then and I keep being surprised because I have low expectations of its adoption, but then suddenly there are big things like Dropbox rewriting their synchronization core (Nucleus) in Rust, and Microsoft releasing Rust bindings for their next-gen Windows API (WinRT).
So I guess it's still rare in the very large realm of software development, but probably more popular than one may expect for a bit of a fringe language. One that I first thought would remain a proof of concept and point of discussion in computer science circles. I'm happy to be wrong because I think it does solve some long standing problems in certain projects.
The compiler does nothing to prevent data races over shared memory IPC mechanisms across processes, which happen to be the way everyone that cares about security is turning back to, concurrency + processes.
I just wanted to mention that I don't believe memory leaks are technically considered memory unsafe, and so while a leak is generally more difficult to create, it is possible to do in safe Rust vs. something like use-after-free.
> I remember trying it out back in 2014 and saw nothing of interest, as a Haskell user.
this isn't surprising; it's a very different kind of language.
> A lot of the main "selling points" of Rust are getting introduced in C++.
kind of true, but also really not. features from rust are indeed being copied to c++, but imo the key difference between the languages is the attitude towards memory safety. memory safety is "opt-in" with c++, while it is "opt-out" in rust. I doubt this will change anytime in the near future.
I guess Rust related stuff is often ranked high at HN (and reddit/programming) for mainly two reasons:
1. It is a very interesting language. Personally I find it fascinating (coming from imperative languages). Some blog posts are really worth the high ranking.
2. It's an enthusiats language. They tend to be emotional on their subject. That's why Rust is indeed overpromoted compared to other languages. People getting their stuff done are often using boring tools like Java, C/C++ or Go and are not hanging around here in language threads very often.
Rust is not prevalent in the industry at all. People reporting how it's used by Microsoft and Amazon etc. are not telling lies, but often forget to see the big picture. Compared to C/C++ or Java Rust's usage is tiny. Less than 1%. I'd say even less then 0.01 % worldwide in terms of people using it, lines of code written daily, project budget spend on. But how often are people writing posts "Why we adopted Java" or "How we improved backend performance by 300% using C++"? Rust is kind of vocal, so the overall picture of it's usage is quite distorted.
Still, the usage of Rust has been going up the last 5 years. So it is gaining. And it's a great fit for certain tasks.
The "you can't print arrays larger than 32" thing was a common papercut for beginners. You could usually work around it with slices, but until now that's been yet another thing to memorize on day 1 or week 1. It only takes a couple of papercuts to go from "this is challenging but fun" to "this is pointlessly frustrating", so I'm always very happy when one of the big ones gets fixed.
Vectors can already be printed. Vectors types are only dependent on the contained elements, whereas Arrays types depend (until const generics come) both on the size of the array and the contained elements.
So whereas a generic implementation of Display for Vec of any length was possible, Arrays required one implementation of Debug per possible length of the array.
Fortunately under normal circumstances it's almost always better to use a Vec anyway, and this is encouraged by the Book. Having done projects in Rust for the past ~2 years I didn't even know this limitation existed, because I hardly ever use plain arrays.
IMHO, it's a chicken and egg kind of thing; it's way easier to use Vecs because arrays really need const generics to actually be useful. I am interested to see how this balance changes.
I know for my no_std work, const generics for arrays can't come fast enough.
Especially when passing a number of things to a function, and you need to pass ownership. You can't do that with a slice, because that's borrowed, so a Vec used to be the only option. Now there's a one option more that's going to be more lightweight than allocating a Vec :)
I've often used the `arrayvec` crate for things like this. Actually one of the things I'm most excited about, once const generics is ready, is seeing something like `arrayvec` in the standard library.
Array sizes are static and can be used for optimization, that's a very significant advantage in some situations. As an example, if I'm emulating some RAM module of a console, I'd much rather use a cheap [u8; RAM_LEN] than a Vec that would incur all kinds of runtime checks for nothing.
Unfortunately there are many limitations with arrays in Rust at the moment, which make it more annoying than it should be. Generics over the array size is one of them, although unfortunately there are others such as https://github.com/rust-lang/rust/issues/53827
No it isn't! I'm sick of dealing with Vec's that do need to be a defined length to be valid, but could be any length! That's what a type system is for! But nobody uses the proper type for that, because actually working with the proper type is so awful!
It's the single worst aspect of working with Rust. I am very happy.
I would disagree. There are a lot of data types which are a fixed length array of bytes (e.g. encryption keys), which make more sense as a value, rather than a container type.
You make the sacrifice of a "punishing" compiler, and spending more time writing code, but you gain software that's more likely to be "correct", safe, and not leak memory.
Really, as in all things computers, it depends what your set of requirements are as to what trade offs you make
Although I haven't verified this myself—I'm not much of a Rust wizard—I've heard that Rust's type system and borrow checker allows you to spend less time total, because your debugging time drops significantly.
Verifying that would be incredibly difficult/impossible but I've personally found that rust has on multiple occasions prevented me from making a mistake that would have cost likely hours to debug that I was instead able to fix in a few minutes.
Fans of static type checking use that as a rebuttal against "but I'm so much more productive in $dynamically_typed_language". I've also seen it in discussions about Scala, Haskell, TypeScript, etc.
Intuitively it makes a lot of sense, and it jives with my subjective feelings (especially for longer-lived projects), but I'm not sure if there have been any studies to back up that claim.
I am not aware of any studies that definitively show an advantage for static or dynamic typing; there have been ones that didn't provide anything conclusive, and you could argue that the experiment design wasn't representative.
From an organizational perspective, the benefits of static type checking are quite obvious. I'd go so far as to say it's a software engineering best practice to prefer languages with static types when building new software. The reason for this is because many, many, many engineering hours are saved at an organizational level by having a compiler that is able to enforce correct data structures throughout the application. Eschewing types for speed is just a form of technical debt that has to be repaid in perpetuity whenever it comes time to maintain or refactor the software, not to mention the fact that every new developer working on the application is now burdened with this debt.
Perhaps compared to Python, Ruby, and vanilla ES6; there is a class of epsilon-from-typo bugs that modern statically typed languages catch and that force rigorous Python and Ruby shops to waste time writing vast batteries of tests for the most banal functionality in their codebase ("does this setter set?").
Moving from one of those languages to a modern statically typed language can come with a feeling that your programs tend to run successfully as soon as they compile --- certainly they do more often than they would in Ruby. Because the compiler is doing some of that testing work for you. It's a good feeling.
But it hasn't been my experience that the extra rigor in Rust's model produces the same boost from Java or Go; in fact, my first-run experience with Rust is generally less reliable than Go; there are a few more things that can go wrong with a Rust program and slip by the compiler.
Which is what you'd expect; Rust isn't garbage collected, so the programmer has to do the garbage collection, and every program is responsible in some sense for designing and implementing that part of its own runtime. There are things you can get wrong there that GC'd languages don't make you bother trying to get right. And, of course, if you take the time to get your per-program runtime really right, there's a performance gain to be had from doing so. It's all tradeoffs.
> Which is what you'd expect; Rust isn't garbage collected, so the programmer has to do the garbage collection, and every program is responsible in some sense for designing and implementing that part of its own runtime.
This seems like a really odd thing to say in at least two dimensions. Firstly, I would say that "the programmer has to do the garbage collection" isn't an accurate way to describe Rust. Secondly, and particularly in comparison with Go, Rust's RAII applies more universally than Go's garbage collection. How many times have you seen a missing defer or a defer inside of a loop? I've seen both quite a bit. But those bugs basically don't happen in Rust precisely because the programmer almost never needs to deal with destruction directly. It's done automatically by the compiler.
As to the broader point, I would very strongly oppose the notion that Rust is less reliable than Go. While I don't think I would argue that Rust gives the same boost over Go that, say, Java or Go give over Ruby (that's a tough argument to make anyway, even if I agreed with it), I would say there is a meaningful boost. But that's a tough argument to make too. Aside from the obvious bits (nil errors and forgetting to check errors), for me, it basically comes to my belief that Go punishes its practitioners for abstraction. I wrote a bit more about that here: https://users.rust-lang.org/t/what-made-you-choose-rust-over...
The RAII vs. defer thing is a point well taken. Also, the kind of thing that doesn't show up in the totally-informal "how many of my post-hoc tests pass the first time I get them to compile" metric.
Another point, which to your credit you didn't make but which is nonetheless true, is that I am not a good Rust programmer!
I don't think Rust is less reliable than Go in the large. I do think there are more things you have to get right in a Rust program than in a Go program, so it's "less reliable" in that weird twilight state when you're first bringing up your program.
I do agree that Go punishes practitioners for abstraction. If you're, like, you, that's a very bad thing. If you're working with a team of people for whom the project is a means to an end and not a brilliant-cut gemstone, putting the brakes on abstraction can be a good thing, which I think a lot of Rust programmers will quickly learn after the nth time they've had to do an edit-compile cycle just to `let () = something` to figure out a type.
FWIW, my experience when it comes to Go and its abstraction limitations _is_ in the context of collaborating with others on a team at work. That's where we feel the limitations of abstraction pretty acutely. Because it means we, to a lesser degree, cannot build APIs that are harder to misuse.
The point about having too much power is taken, and I don't have experience with that in a team context. And yeah, I am not exploring the Rust downsides as much here, and there are definitely others.
Curiously, I’ve never seen anyone in forums take this premise to its logical conclusion and advocate the use of a language which has a much more powerful type system than Rust does, like dependent or refinement types. Haskell, Scala, Idris, and others (even Python with crosshair!) natively or with extensions support enforcing some very sophisticated constraints on a program. Tagged unions and interfaces are hardly the limit of what’s possible. However, I don’t think cutting edge type systems, theorem provers or other sophisticated tools for program validation have demonstrated enough business value to receive widespread adoption outside of only some (albeit very important) market sectors.
Really, I think most argumentation comes down to “the type system of the programming language I want to use anyway is the right one, and everything else is wrong”.
I lump this into the category of learning how to best apply your tools to the right situations.
If I'm slinging together an internal CRUD app, the extra time thinking about types probably will slow me down because the major risks aren't really type problems.
But if for example I'm working on something in a game engine, being able to use the language and compiler to enforce assumptions/invariants at compile time is really useful.
There's some tradeoff there though. I'm doing most of my dev in C++ and run all the time with ASAN / LeakSanitizer. I have a leak-causing mistake, what, twice a year maybe ? Definitely not something worth loosing sleep over.
The much bigger problems imho are not really tractable. e.g. consider something that does:
there's no "leak" in the OS sense as everything will be reclaimed upon server::~server... but your memory usage will be strictly increasing. Can Rust detect those cases ? As in my experience this is the most common leak pattern (of course not in a simple example like this, but when your "server" architecture starts being split across multiple threads and source files...
Rust does not attempt to stop leaks. It does make it less likely to happen, but completely preventing them is not even on the table.
This particular case might be because you would probably consume the vector in the loop, which would free, but you could absolutely write the exact same code in Rust with the same end result.
In your example you are iterating over `m_requests_to_process`. As you are using `auto` instead of `auto &` it automatically clones the elements of a vector. In Rust, it's not possible to clone an element by accident. Objects are moved by default (think `std::move`) and if you want to clone instead you need explicitly use `.clone()`.
If `do_stuff` function takes ownership of a request (as in, if it takes `Request` parameter and not `&mut Request` parameter) than the problem will be pointed by Rust, and so the compiler wouldn't allow you write code like this forcing programmer to write code that removes elements from the list to take ownership of them. For example:
for req in mem::take(&mut self.requests_to_process) {
do_stuff(req);
}
`mem::take` (https://doc.rust-lang.org/std/mem/fn.take.html) replaces an object behind a mutable reference with a default value for a given type (empty vector in case of vectors) and gives you an ownership over vector.
struct request {
int id;
double parameter_value;
};
surely moving does not gain anything - the original vector object still keeps the memory allocated, right ? sure, if you have complex requests with substrings, etc, but in that case I'd have `const auto&`-ed :)
(from my experience going full-throttle on movability when C++11 came out, I'd say that this was a mistake overall, much better to keep things as const& most of the time if you can. I've not yet reached a state where I consider the need for ownership transfer a code smell... but not very far :-))
With the implementation your parent posted, the entire vector would be replaced with a fresh one. So the original vector would be deallocated entirely.
You could also do something with the drain method (which they posted originally and changed to the current implementation, not 100% sure why) and that would keep the memory around, yes, but then you'd be with only the high water mark of the number of requests, because it would be re-used.
You don't even need `std::mem::take`, you can just `drain` the `Vec`. This also allows the allocation to be reused, unlike your `mem::take` which ends up dropping the allocation.
For "non strict mode" meaning bypassing the borrow checker, rust already has that and it's called "unsafe". And if by "non struct mode" you mean not having manual garbage collection then you've already got that either as `::leak` letting you leak memory or various GC implementations like https://github.com/Manishearth/rust-gc/.
One must write structure a program in a way that is compatible with the borrow checker. It would probably require a rewrite to make unsafe code that was purposefully ignoring the borrow checker then pass the borrow checker.
Keep working at it. After a while you start to realize the patterns that work. Now, I can prototype in Rust pretty fast.
In theory yes, but in the way you're probably thinking about it, no. Unsafe doesn't turn off checks, it adds features that are unchecked. So you can't just toss in unsafe blocks and get the compiler to let you do whatever you want, you have to actually write different code.
I don't have much experience, and thus might be wrong, but one thing I've internalized is that it's often better to just use Rc<T> (reference counted smart pointers) or a garbage collector to make forward progress rather than fight the compiler trying to get lifetimes straight. You can make it more efficient later, but it's often not to worth the bother.
I'm curious at how prevalent this opinion is among Rust developers; it seems to me that it would defeat the point of using Rust at all and go with a strongly-typed GC language.
Personally, I think once you get to a certain place, you don't end up doing this super often. I do think that it's a good idea whenever you feel like you need to do it, and it can help you get enough experience with the language to be able to get over that hump.
There are also a lot of nice features in Rust other than the pure absolute runtime efficiency.
Thank you — it’s good to know that. Rust is not my first-choice language; which means that I tend to use it when I need something non-trivial and low-level. The problem, of course, is that it colours the experience, so I’m always curious to listen to other experiences.
> There are also a lot of nice features in Rust other than the pure absolute runtime efficiency.
Certainly! I reread my earlier post and see how it could have been more qualified; what I meant to say was if the burden of the borrow-checker is so high that the user needs to use the ~Rc~ by default /I/ would find it hard to justify Rust for /that/ type of programming over other languages with similar features.
A single well placed Rc<RefCell<T>> can simplify a design immensely, and it is made obvious if it is present in a performance critical path. 99% of the time, though you don't use it because either the design is simple enough not to need to deal with multiple ownership, or the performance requirements make it so that you have to figure out how to avoid it.
> it seems to me that it would defeat the point of using Rust at all and go with a strongly-typed GC language.
This seems to be a regular comment when talking about Rust (among other "incremental improvement" approaches) and I don't really get it: for 95% of the code things are simple enough that you don't have to think too much about it, the other 5% can either rely on one of the multiple-ownership safe abstractions or use unsafe. To me this framing sounds like "why do I need seat belts, if I fall of a cliff it won't do any good!"
The parent comment insinuated that it was used more than 1% of the time; which also mirrored my (brief) time using Rust. I spent long periods of time as a beginner trying to get a good program structure past the borrow-checker only to discard the work, when I realized that certain problems introduced different ownership constraints. Of course, part of that is due to the fact I haven’t spent a prolonged period of time in Rust. I imagine that it would get better with time; hence I was surprised to hear an opinion that used ~Rc~ by default.
> To me this framing sounds like "why do I need seat belts, if I fall of a cliff it won't do any good!"
I’m sorry that that’s how you read my comment — the languages I tend to use are much higher-level than Rust, like Haskell and Lisp, and I constantly found myself having to restructure my code to deal with memory-management. In particular, I found closures and futures difficult to work with — compared to a higher-level GC language. This experience may or may not be typical; I have few data-points to draw on after all!
I agree. Rc+RefCell or Arc+Mutex and cloning values instead of passing references are much better escape hatches when the borrow checker complains during prototyping than going for unsafe.
Though I have found that the borrow checker often ends up pushing architectural changes on me as I'm fixing things.
This might be because I'm just newer with Rust, but I personally would find resolving 30 borrow-checker issues at once to be much more painful that resolving them as I go.
Sure, if by "smaller issues" you mean a likely redesign of large parts of the codebase. Think of it like writing C code without bothering to free any memory.
In C having to free allocated memory manually dictates how data structures are built and how your program flows. Often you have to keep track of extra state in order to free your memory at the right time and if you don't have this in mind from the get-go you're going to write code that leaks a lot of memory or doesn't work. The same is true for rust, but instead of the code leaking or not working it doesn't compile. If you turn those checks off with unsafe you're essentially stuck with an improved C that's just as difficult to prototype in.
Systems programming languages are simply not designed for fast prototyping, instead focusing on fast and in rust's case correct code. A GC, dynamic typing, high level library, etc. all help you prototype faster but you're not going to be able to use them to write an OS or a web browser.
I think looking at it as "punishing" is the wrong mentality.
Rust has very strict checks, and we -want- those. That's why we're using Rust in the first place.
I -could- use a more convenient / lenient language like ruby or golang but I generally wouldn't prefer it unless the use case made sense. You end up "paying" in different ways later down the line as your codebase grows in complexity.
In my experience, the rust compiler is extremely helpful and most issues I had went away after reading the rust book. [0]
I take your point, but Ruby vs Go isn't a reasonable comparison. Rust is a lot stricter than Go, by necessity (no GC), but they're both statically typed languages.
The difference between {Rust, Go} and Ruby is almost entirely about rigor.
The difference between Rust and Go, on the other hand, is mostly pragmatic: Rust is "strict" about design and implementation details Go's runtime handles for the programmer; there's nothing for Go to be "strict" about.
Rust is more rigorous than Go, but to a much smaller degree than Go is over Ruby.
Regarding memory management, sure, but Rust's strictness also handles things that Go doesn't. Two examples are error handling --- Result<T,E> in Rust ensures that you check for errors before using a result --- and thread safety --- the borrow checker prevents many synchronization errors that Go doesn't catch.
I like Result<T, E> and would welcome its addition to Go, but the most surprising thing to me about switching from Go to Rust is that error handling is basically identical in practice; a little less typing in Rust, though you weigh that against Go's (weak but convenient) universal error interface.
Rust gives a hard shove where Go gives something closer to a nudge, but in practice, Go's nudge (the requirement to do something with an error or else bear the shame of _'ing it) gets me 99.999% of the way to Result.
> Go's (weak but convenient) universal error interface
Rust has an `Error` trait too, and you can always just use `Box<dyn Error>` as your error type if you're fine with using a heap allocation to avoid caring about the concrete type (which is what's happening in Go when you use the error interface as your return type anyhow). Rust just makes the more efficient (in terms of runtime performance, not necessarily usability) the default and the more expensive option opt-in. This is a common pattern in a lot of the language design choices: performant by default, with the option to opt into a less performant variant to make things easier if you're okay with the cost. I think a lot of newcomers to the language (not saying you are one; I have no idea what your experience level is) don't realize this because the Rust community tends tends to _really_ like doing things the "proper" way. This isn't a problem per se, but I think there would be immense value in having more resources that made all of the options on the performance versus usability spectrum more clear.
> Rust has very strict checks, and we -want- those. That's why we're using Rust in the first place.
One could argue that outside of safety guarantees that Rust provides it also happens to be a good language that people would like to use for those reasons (I really like the tooling, ecosystem and standard library when it comes to Rust, not so much the language itself).
It probably won't. Rust's unique selling point is in a combination of safety, performance, and minimal runtime (no garbage collection). The most difficult aspects of Rust are a direct consequence of safe minimal-overhead memory management, and we don't know how to do much better without adding a GC/runtime or compromising safety.
Rust has many quality-of-life improvements that could be transplanted to a more relaxed GC-based language, but that won't be Rust any more. Here are some musings how such language could look like:
A big part of life is accepting people will disagree with you.
Ultimately I did get a solid explanation here. If I want a quick, fast and dirty language , Rust doesn't fit the role.
I really do love how C# gives me the ability to hack things together quickly, with still stopping me from doing things which simply won't work. Flutter takes this even a step further with the dynamic keyword( C# has dynamics too , but they aren't as robust ). I can write function calls with dynamics which is great for prototyping. Eventually bad things will happen if you use dynamics in every single call , but it helps me get started.
I definitely understand why many software developers see this as a dumbing down of software engineering, which I would attribute the downvotes to.
There is definitely an aspect of the 'the appropriate tool for the job'. We use Python extensively for 'glue' scripts on my project (build, deployment etc.). It's wonderful and convenient for programs that are 10 to 1000 lines long. The result is clear and predictable and maintainable (particularly when compared to a bash script alternative).
I would never write a large production app in Python though (having done it a couple of times). The flexibility becomes unwieldy at that scale and you have to have tests that exercise every corner or you'll have problems hours or days into running.
Yes. How much varies though. There are certain classes of test that take time in a dynamic language that you just don't have to write. They're unimportant 'just check I'm actually passing a list to this function and not a class' type tests rather than interesting algorithmic or behavioural tests.
> (OT: why OP got downvoted so hard? It's a reasonable question to ask)
I find myself upvoting tons of comments in Rust threads where those comments are just asking questions, but are, for whatever reasons, downvoted. Happens on the Rust subreddit too. I don't know wtf it's about.
Rust doesn't overlap with the application areas of those languages. It is a complement as systems language to those web / backend / script languages.
I too don't like rust evangelism around here. But any developer should know at least one system language, understand algorithm complexity, understand basics about memory management and caches, etc.. And probably it is easier to learn rust than C++ to those with web background.
The eagerness of the compiler to point out your mistakes is the quintessential feature of rust, if this is something you don't want to deal with then almost certainly rust isn't the language for you.
With that stated, I would encourage you to understand why rust was designed this way and why the strictness of the compiler is actually a major advantage compared to languages like Python and JS where a lack of a type system actually makes the software much more prone to bugs.
The main way to make the compiler more forgiving is to use Rc<RefCell<T>> (and Arc<Mutex<T>> when you need thread-safety) to move borrow checking from compile time to run time. Please avoid doing that, though --- it's ugly, it can be slow, and the borrow checker is usually yelling at you for a good reason.
There are also some immature crates for tracing garbage collection in Rust, like rust-gc, which typically has higher throughput than reference counting.
One thing you can do is wrap everything in Rc<>. This makes the held object reference counted and you can copy references around as you feel like. This gets you ownership much more like what you would get in a GC language (e.g. C#, Python, JS), but in an opt-in fashion.
In my experience, I did eventually start to appreciate the compiler forcing me to address things up-front.
But as an aside, if you're just trying to prototype quickly, some useful "shortcuts" can be:
- Using `Box<dyn std::error::Error>` for functions which need to handle a result
- Using `unwrap` or `expect` to easily access Option internal values
- Using `unimplemented!()` if I'm trying to mock an API design. This one is nice because it will let you build out the signature including specifying a return type without having to build out the function logic.
Obviously these are shortcuts and not production suitable, but can be good to just get some code on the page that compiles
I have used that langs and much more. Rust was the hardest lang I have learned by a long shot. The first 2 months was kind of miserable (more because I try to build a interpreter that hit some of the hardest stuff on rust, and not knowing how deal with them).
In retrospective, I make the life harder:
* Thinking like in the other langs I have learned (with exception of my first 2) is enough to just read fast some tutorial and start coding... I relent and pick a book to learn it right.
* Start worrying so much about performance, when never before I bothered much (what! this can ALLOCATE!). Now I clone like madman!
* Try to fit the OO mentality to Rust. Even in F# I don't fully commit to POCO objects and functions as I do in Rust now. Also, the lacks of polymorphism is what hurt me sometimes (ie: is harder in rust)
But now? Rust feel ABSURDLY productive. The thing I notice is how much I refactor stuff now, (even more than in F# that also was a up in this regard).
The "punishment" parts is only in my toy interpreter and very small now. For my main project ( a ERP/ecommerce app rewrite from F#) Is great!.
You might enjoy trying OCaml! Most of the "necessary and hard" strictness of Rust comes from not needing a garbage collector. Once you have that, it's a lot easier to provide something rigorous but more ergonomic.
A classic example of them being used in Rust is to be generic over arrays, like what the blog post talks about. Arrays encode their length as part of the type; [T; N] is the type of an array of Ts that’s N elements long. In other words:
fn foo<T>(array: [T; ?]) {
How do you define ? to work on any length? When const generics is implemented, you’d write
fn foo<T, const N>(array: [T; N]) {
and now you can call this function on arrays of varying lengths.
There's a bit more to it than that, but that's the core of it. Does that help?
So in Rust you must specify the length of an array? How do you implement map, fold, etc., that work on an entire collection regardless of length? Is that a different API in Rust?
> So in Rust you must specify the length of an array?
Correct.
> How do you implement map, fold, etc., that work on an entire collection regardless of length?
So, while arrays exist, there are also things like slices and vectors, which have a runtime length, rather than an array's compile-time length. So that's one bit of it. But the other part is that these are implemented on iterators, rather than on the type directly, so usually what happens is "turn that into an iterator, then do map/fold/filter/whatever on the iterator. It is easy to convert the compile-time length array to a run-time length iterator, so it ends up being a uniform interface.
You would still need to create an iterator, because there are three different ways to iterate, and you have to pick which one you want.
However, often times it entirely compiles away to nothing. What this may help with is bounds checks; it is possible for this to make it easier to remove/hoist bounds checks in some circumstances. In my understanding.
It allows you to be generic over things that arent types is my very dumbed down description of what it means for most people. One of the biggest missing features for me from C++ doing scientific computing.
Great to see that const generics has become mature enough to be exposed by the stdlib, even if it's not ready for full stabilization just yet.
People wanting to use const generics themselves will want to follow this issue, which is about stabilizing a minimally-useful subset of const generics: https://github.com/rust-lang/rust/issues/74878 . It seems to be coming along well: "Regarding the remaining steps: there are only a handful of issues I think are blocking min_const_generics at this stage (several of which already have open pull requests). We also want to add many more tests, which is something anyone can easily help with. If you want to tackle one of the issues, feel free to ask @lcnr or myself how to get started; I'm going to dedicate time to fixing whichever remain."
To me, it felt ergonomic, and it was absolutely compile-time type safe to the extent you would expect. It worked just fine. I know of quite a few other libraries that do the same thing with their own unique approaches, so there are many flavors of this that all work on stable Rust.
If you're asking for something like this to be built into the standard library... that's very unlikely to happen in the foreseeable future.
i'm actually asking for something like this to be implemented on a language level, but if the type system is ergonomic enough, then i'd be mostly fine.
most numbers i deal with in my code have units somewhere. mistakes happen. i don't want to choose from multiple unique approaches, i need the ecosystem to standardize on one solution. see also the async task executor problem in other languages.
When working with audio signals, is it possible to transmute a Vec<f32> (or slice/etc.) to a Vec<Amplitude<f32>>? It's a concern I haven't seen addressed the last time I tried working with Rust units.
I would just recommend constructing them as Amplitude values to begin with and avoid the need for a transmute. That Vec isn’t going to materialize out of thin air, and there’s no cost to using one of these zero size wrappers from the beginning.
But, yes, you probably could transmute it... if you really have to.
These scenarios are so hypothetical that it’s pointless to give an answer. What if the library requires you to pass in values in a custom struct they defined? What if the library requires all sorts of arbitrary nonsense?
The reality is that the conversion cost to just `map` one Vec to another is extremely low on anything but maybe a microcontroller —- and even then, maybe not.
If you benchmark things and can’t find a safe way to do things, there are unsafe methods, but I am highly skeptical that they would be needed.
It’s also extremely possible to fork such a library, change a few lines to use the dimensionally correct type, and be done with the issue.
If all of your heavy lifting is being done by this external library anyways, and that library just uses raw floats... why bother with the ceremony of using a dimensionally safe float in your own code if the performance cost of mapping between types is too high?
The real benefit of dimensional safety is when you’re actually doing computations, not some external library you pulled in.
Rust allows for these zero cost wrapper types that will do exactly the correct computations as if they were done with hand written code... but you have to actually have computations that you want to do. The type system intentionally doesn’t want you to treat these typed floats as regular floats. That’s the point.
But there are ways to transmute certain types for no performance penalty, they’re just footguns you should avoid except as an option of last resort.
> These scenarios are so hypothetical that it’s pointless to give an answer.
Every audio library I've seen so far operates on (*i16/f32, size_t) or &[i16/f32]. An audio library which supports dimensioned values is hypothetical. And most dimensioning libraries don't have a unit of "sample" (in time) or "audio amplitude" or "channel", so the audio library won't even know what unit system to pick.
> It’s also extremely possible to fork such a library, change a few lines to use the dimensionally correct type, and be done with the issue.
And suddenly the audio library is tied to your specific unit system, and you still need transmutes or whatnot when talking to the OS sound library.
> If all of your heavy lifting is being done by this external library anyways, and that library just uses raw floats... why bother with the ceremony of using a dimensionally safe float in your own code if the performance cost of mapping between types is too high?
Most of the time I end up just using a named type alias, instead of a strong newtype.
I gather from the description that this is not enabled by default. Is that correct? If so, why isn't it enabled by default? It would seem to fit with Rust's focus on safety.
Because it has no purpose in normal Rust code as it's already guaranteed to be safe. It's only purpose would be on {unsafe code, inline assembly} and is just a performance penalty otherwise.
There's no formal verification that uses of unsafe in the standard library are correct and there are no bugs. (Of course goes for other libs too). So this justification doesn't really feel robust.
Other than "it looks like maybe it made up some of the compile time loss from LLVM 10," I'm not aware of anything specific.
In general, we update regularly to gain access to bug fixes and because upgrading regularly makes each upgrade easier, so we don't need something specific to trigger an update.
This marks the first Rust release than can be directly installed with plain old rustup.sh on RISC-V Linux distributions (like Ubuntu, Fedora, and others). A very awesome milestone and my thanks to all who contributed.
All the core tools I tested works 100% and almost all the crates work. A few, like libc and Nix, still needs explicit riscv64 support.
210 comments
[ 3.0 ms ] story [ 289 ms ] threadHowever in this case, these are const generics, so you no longer need to use macro to generate [char;1],[char;2]...
You just write [char; N] where N is const. Compiler determines which values of N are used and generates for them an appropriate version.
Const generics still aren't stable. I believe the GP was talking about the list of new `const fn`s in the release (near the bottom of the post).
Basically, const functions can be used in places regular functions can't because they are directly "run" by the compiler at compile time.
Const functions let you run code to define const values. This code will be run at compile time before the const values are inlined.
For example let's say you have a constant for PI but you need 2*PI. You could do the multiplication in const context, meaning the multiplication will happen during compilation and not execution. Slowly more and more of the language is becoming usable in const context so more advanced things can be done during compile time.
Compile-time function evaluation is also one of the features of the Jai programming language. If you can't get people to use your language, getting them to steal all your features is a good consolation prize.
It's difficult to use Jai as long as it only exists on Jonathan Blows computer, he still hasn't released it
Making them const just formally guarantees the compiler can evaluate them at compile time which makes them useful in more contexts.
Was the point of my answer.
`const fn` is merely an API guarantee that the function can always be evaluated at compile time if the arguments are themselves known at compile time.
> When you have a small team that write tests and a compiler that is on the boundary of interactive speed, compiler speed dominates performance.
https://crawshaw.io/blog/fast-compilers
e.g. char::is_ascii_alphabetic() was made const in 1.47, but 1.46 would already run it at compile time: https://godbolt.org/z/hWf65f
The difference is in 1.47 you can use the result to assign something that must be known at compile time.
Haskell is cool and all but every time I tried it, I spent too much tle with package management for example.
Also you can't really ship an app in Haskell.
Frankly, I did nit see people releasing proper desktop apps for some time now :( Electron apps do not count.
https://www.areweguiyet.com/
That said, it is really really tough to get all the little behaviors that people rely on. Including all keyboard shortcuts (across all platforms), double/triple clicking words, etc.
I guess that this may not matter that much for specialized software (but please focus on the damn textboxes, they are hard to get right). I'm just commenting in case other people decide to follow the same path.
Also, for a broader audience: be mindful of accessibility. Usually, this kind of thing doesn't work with screen readers.
Another problem is with the package ecosystem. It feels like most of Swift ecosystem is wrappers around Apple APIs.
There are also some strange workflow edge cases like the fact that you currently cannot use Metal shaders in Swift Packages. This is due to the fact that Xcode projects are supposed to be ephemeral (not version controlled, but generated when you clone the package). However if you want to compile Metal, you have to add some compiler options. As a result, every time you add a dependency, you have to regenerate the XCode project file, you have to fuck with your XCode project to add the compiler options.
Ironically, you can use Metal in Rust crates just fine.
Rust is also truly cross-platform. Windows and Linux have first class support.
To summarize my argument, Swift feels like daddy Apple is telling me what is best for me which may or may not be true. Rust feels like true empowerment. If there is something that doesn't suit me for whatever reason, there is generally a way of working around it.
I guess it depends on what you are doing. Swift is OK for iOS/macOS development but other than that Rust is more advanced. Rust macros are really powerful, things that need to be added explicitly to the Swift compiler can be implemented as Rust macros.
Yep blazing fast indeed. /s
https://github.com/ixy-languages/ixy-languages
Rust is one of the nicest languages out there. Very well thought out, elegant, full low-level control for real systems programming (sorry Go!) AWS uses it a lot, Microsoft is starting to use it.
Go, or even Python is better if you just want to make a backend API for a (web) app. You'll be more productive with it over Rust because you have a GC and can ignore the whole ownership issue for the most part. Python for very small teams likely to stay small for a long time, Go otherwise.
C++ has come a long way, but it's still hairy and loaded with footguns and baggage. I would only use it these days if I needed to use a big C++ library (like a game engine).
I am no longer willing to use Haskell, PHP, Perl, Java or JVM languages, .NET or anything else that's not Go, Rust or Python. With the same caveat as C++ above. I pass over job postings that require them. That's not saying those languages are bad, just that in my personal opinion there are better tools out there, and if I'm going to work with it day in and day out, it had better be in something at the top of my list - because life is short.
Yes, it is not super massive, but it is in the production path for important things, and appears to be on quite an upward trajectory.
I do not work at Amazon but I do have a couple of friends there both in Seattle and Vancouver.
Everybody wants to look cool from the outside :-), blogging about how you're using Java in 2020 is not cool.
Just think that it took C++ 40 years to reach where it is now and still there are domains where C rules, a systems language that people forget is only 10 years younger than COBOL.
It has been picking up a lot lately, Apple, Amazon, and Microsoft are all showing some adoption, for instance.
> I remember trying it out back in 2014 and saw nothing of interest, as a Haskell user
The interest for a Haskell user would be that you have some of the functional programming features you like (such as discriminated unions!) with C/C++ levels of runtime performance/efficiency. For someone who has no need for that level of low level control/performance, Rust is generally not a language you want to use, for sure.
> A lot of the main "selling points" of Rust are getting introduced in C++
It is true that you could, with sufficient coding standards and linters, get something approximately like Rust by using C++, where the linter disallows any non safe pointers, any use of null or nullable types without wrapping it in a variant, disallowing all cases of undefined behavior, and so on. However you would have something a lot less pleasant to use than using Rust in the first place, build times would probably end up worse with those thing enforced as a build step, and setting up such an environment would be non trivial and you basically are just building rust out of C++, twine and gum, so why not just use Rust?
my 100% honest answer: Rust does not have Qt
rust-qt[0] doesn’t support inheritance at all, so you can only connect to things in Qt which accept callbacks using slots. It requires `'static` lifetime on those callbacks, so as far as I can tell you are also forced to use `Rc`s whenever you are doing something with a rust-qt object even if you should be able to use a plain reference. The code is auto-generated, so all APIs are `unsafe`, and no function overloads means names are gross because the function signatures must be expressed in the function name (e.g. the `QMessageBox` constructor[1] becomes `QMessageBox::from_icon2_q_string_q_flags_standard_button_q_widget`). This could be improved by using `Option`s for arguments with defaults, but right now this is what it looks like.
qmetaobject-rs[2] has some support for inheritance but as far as I can tell it’s limited to a couple of base types only. It’s also designed around using QML, so it doesn’t actually expose most of the Qt API. As such I don’t have too much experience with it.
I’m unaware of any other usable Rust Qt bindings right now. I currently make things work by using rust-cpp[3] to create C++ subclasses and smuggle events, but this sucks because it also means the objects on the Rust side need to have the Qt API re-exposed manually since AFAIK there isn’t a way to have Rust handle that without using a code generator. (There might be some hackier ways to make this work, or maybe someone with more Rust experience than me knows of something smarter that could happen, but in any case the ergonomics right now are not good for the average developer.)
[0] https://github.com/rust-qt/ritual
[1] https://doc.qt.io/qt-5/qmessagebox.html#QMessageBox-1
[2] https://github.com/woboq/qmetaobject-rs
[3] https://github.com/mystor/rust-cpp
https://github.com/woboq/qmetaobject-rs
https://archive.fosdem.org/2019/schedule/event/rust_cpp/
https://woboq.com/~olivier/fosdem2019_rustcpp/
The 1.0 release was in 2015, and there wasn't much industry adoption before that. Today most of the big tech companies are using it for something. Microsoft published a series of articles this year about using Rust internally.
> A lot of the main "selling points" of Rust are getting introduced in C++.
In the C++ world, Rust's biggest selling point by far is memory safety. The safety story is based on notions of ownership, borrowing, and lifetimes that are built into language itself. Most of the standard library, and many foundational crates like Serde and Rayon, have designed their APIs around these concepts. These things tend to go "all the way down": Every single function or datatype that deals with references defines its lifetime requirements for those references, and every single callsite of one of those functions or variable of one of those types is statically checked to meet those requirements.
I wouldn't go so far as to call this an "all or nothing" thing. Languages like TypeScript make it clear that you can get good value out of a strict type system even when much of the ecosystem isn't strict. But there's a lot of value in being on the "all" side of the fence here -- especially for languages with pointers and destructors -- and I think it's unrealistic to expect an established language like C++ to make that many changes.
> Also, what is it about Rust that gets automatic top view on HN?
Having a new, serious contender in the C/C++ world is exciting! And maybe Rust being more challenging to learn makes people more excited about being "in the club" when they do learn it. On the other hand, all the talk about memory safety seems to make some people kind of...self-righteous?...about the language, which we would all prefer to see less of :p
Now that the application has grown to be quite huge I really struggle to maintain the python code, I miss static typing. Big code changes are a pain to validate and regressions always manage to slip in the cracks. Meanwhile I've used Rust for some smaller, less critical components and it Just Works. C-tier perfs and zero crashes in 4 years.
I'm definitely optimistic for the future of the language at this point. The community is thriving and the language keeps improving, while not breaking backward compatibility and forcing me to update my code to make use of the latest features.
[1] https://github.com/tomv564/pyls-mypy
As they stand I thought that they took a lot of effort to write and maintain for very little practical value.
As for use in the industry, I see it mentioned every now and then and I keep being surprised because I have low expectations of its adoption, but then suddenly there are big things like Dropbox rewriting their synchronization core (Nucleus) in Rust, and Microsoft releasing Rust bindings for their next-gen Windows API (WinRT).
So I guess it's still rare in the very large realm of software development, but probably more popular than one may expect for a bit of a fringe language. One that I first thought would remain a proof of concept and point of discussion in computer science circles. I'm happy to be wrong because I think it does solve some long standing problems in certain projects.
Obligatory clarification: the compiler enforces that there are no _data_ races; there can still be other types of race conditions.
The compiler does nothing to prevent data races over shared memory IPC mechanisms across processes, which happen to be the way everyone that cares about security is turning back to, concurrency + processes.
this isn't surprising; it's a very different kind of language.
> A lot of the main "selling points" of Rust are getting introduced in C++.
kind of true, but also really not. features from rust are indeed being copied to c++, but imo the key difference between the languages is the attitude towards memory safety. memory safety is "opt-in" with c++, while it is "opt-out" in rust. I doubt this will change anytime in the near future.
1. It is a very interesting language. Personally I find it fascinating (coming from imperative languages). Some blog posts are really worth the high ranking.
2. It's an enthusiats language. They tend to be emotional on their subject. That's why Rust is indeed overpromoted compared to other languages. People getting their stuff done are often using boring tools like Java, C/C++ or Go and are not hanging around here in language threads very often.
Rust is not prevalent in the industry at all. People reporting how it's used by Microsoft and Amazon etc. are not telling lies, but often forget to see the big picture. Compared to C/C++ or Java Rust's usage is tiny. Less than 1%. I'd say even less then 0.01 % worldwide in terms of people using it, lines of code written daily, project budget spend on. But how often are people writing posts "Why we adopted Java" or "How we improved backend performance by 300% using C++"? Rust is kind of vocal, so the overall picture of it's usage is quite distorted.
Still, the usage of Rust has been going up the last 5 years. So it is gaining. And it's a great fit for certain tasks.
So whereas a generic implementation of Display for Vec of any length was possible, Arrays required one implementation of Debug per possible length of the array.
I know for my no_std work, const generics for arrays can't come fast enough.
Especially when passing a number of things to a function, and you need to pass ownership. You can't do that with a slice, because that's borrowed, so a Vec used to be the only option. Now there's a one option more that's going to be more lightweight than allocating a Vec :)
Unfortunately there are many limitations with arrays in Rust at the moment, which make it more annoying than it should be. Generics over the array size is one of them, although unfortunately there are others such as https://github.com/rust-lang/rust/issues/53827
It's the single worst aspect of working with Rust. I am very happy.
Rust seems to be overally punishing
You make the sacrifice of a "punishing" compiler, and spending more time writing code, but you gain software that's more likely to be "correct", safe, and not leak memory.
Really, as in all things computers, it depends what your set of requirements are as to what trade offs you make
Intuitively it makes a lot of sense, and it jives with my subjective feelings (especially for longer-lived projects), but I'm not sure if there have been any studies to back up that claim.
https://danluu.com/empirical-pl/ is a good place to get started on this topic.
Moving from one of those languages to a modern statically typed language can come with a feeling that your programs tend to run successfully as soon as they compile --- certainly they do more often than they would in Ruby. Because the compiler is doing some of that testing work for you. It's a good feeling.
But it hasn't been my experience that the extra rigor in Rust's model produces the same boost from Java or Go; in fact, my first-run experience with Rust is generally less reliable than Go; there are a few more things that can go wrong with a Rust program and slip by the compiler.
Which is what you'd expect; Rust isn't garbage collected, so the programmer has to do the garbage collection, and every program is responsible in some sense for designing and implementing that part of its own runtime. There are things you can get wrong there that GC'd languages don't make you bother trying to get right. And, of course, if you take the time to get your per-program runtime really right, there's a performance gain to be had from doing so. It's all tradeoffs.
This seems like a really odd thing to say in at least two dimensions. Firstly, I would say that "the programmer has to do the garbage collection" isn't an accurate way to describe Rust. Secondly, and particularly in comparison with Go, Rust's RAII applies more universally than Go's garbage collection. How many times have you seen a missing defer or a defer inside of a loop? I've seen both quite a bit. But those bugs basically don't happen in Rust precisely because the programmer almost never needs to deal with destruction directly. It's done automatically by the compiler.
As to the broader point, I would very strongly oppose the notion that Rust is less reliable than Go. While I don't think I would argue that Rust gives the same boost over Go that, say, Java or Go give over Ruby (that's a tough argument to make anyway, even if I agreed with it), I would say there is a meaningful boost. But that's a tough argument to make too. Aside from the obvious bits (nil errors and forgetting to check errors), for me, it basically comes to my belief that Go punishes its practitioners for abstraction. I wrote a bit more about that here: https://users.rust-lang.org/t/what-made-you-choose-rust-over...
Another point, which to your credit you didn't make but which is nonetheless true, is that I am not a good Rust programmer!
I don't think Rust is less reliable than Go in the large. I do think there are more things you have to get right in a Rust program than in a Go program, so it's "less reliable" in that weird twilight state when you're first bringing up your program.
I do agree that Go punishes practitioners for abstraction. If you're, like, you, that's a very bad thing. If you're working with a team of people for whom the project is a means to an end and not a brilliant-cut gemstone, putting the brakes on abstraction can be a good thing, which I think a lot of Rust programmers will quickly learn after the nth time they've had to do an edit-compile cycle just to `let () = something` to figure out a type.
The point about having too much power is taken, and I don't have experience with that in a team context. And yeah, I am not exploring the Rust downsides as much here, and there are definitely others.
Although you could characterize it as "Rust makes the programmer think about and describe the garbage collection."
Really, I think most argumentation comes down to “the type system of the programming language I want to use anyway is the right one, and everything else is wrong”.
If I'm slinging together an internal CRUD app, the extra time thinking about types probably will slow me down because the major risks aren't really type problems.
But if for example I'm working on something in a game engine, being able to use the language and compiler to enforce assumptions/invariants at compile time is really useful.
The much bigger problems imho are not really tractable. e.g. consider something that does:
there's no "leak" in the OS sense as everything will be reclaimed upon server::~server... but your memory usage will be strictly increasing. Can Rust detect those cases ? As in my experience this is the most common leak pattern (of course not in a simple example like this, but when your "server" architecture starts being split across multiple threads and source files...This particular case might be because you would probably consume the vector in the loop, which would free, but you could absolutely write the exact same code in Rust with the same end result.
However, it may help in this specific case.
In your example you are iterating over `m_requests_to_process`. As you are using `auto` instead of `auto &` it automatically clones the elements of a vector. In Rust, it's not possible to clone an element by accident. Objects are moved by default (think `std::move`) and if you want to clone instead you need explicitly use `.clone()`.
If `do_stuff` function takes ownership of a request (as in, if it takes `Request` parameter and not `&mut Request` parameter) than the problem will be pointed by Rust, and so the compiler wouldn't allow you write code like this forcing programmer to write code that removes elements from the list to take ownership of them. For example:
`mem::take` (https://doc.rust-lang.org/std/mem/fn.take.html) replaces an object behind a mutable reference with a default value for a given type (empty vector in case of vectors) and gives you an ownership over vector.(from my experience going full-throttle on movability when C++11 came out, I'd say that this was a mistake overall, much better to keep things as const& most of the time if you can. I've not yet reached a state where I consider the need for ownership transfer a code smell... but not very far :-))
You could also do something with the drain method (which they posted originally and changed to the current implementation, not 100% sure why) and that would keep the memory around, yes, but then you'd be with only the high water mark of the number of requests, because it would be re-used.
`Vec::drain`: https://doc.rust-lang.org/std/vec/struct.Vec.html#method.dra...
Keep working at it. After a while you start to realize the patterns that work. Now, I can prototype in Rust pretty fast.
There are also a lot of nice features in Rust other than the pure absolute runtime efficiency.
> There are also a lot of nice features in Rust other than the pure absolute runtime efficiency.
Certainly! I reread my earlier post and see how it could have been more qualified; what I meant to say was if the burden of the borrow-checker is so high that the user needs to use the ~Rc~ by default /I/ would find it hard to justify Rust for /that/ type of programming over other languages with similar features.
> it seems to me that it would defeat the point of using Rust at all and go with a strongly-typed GC language.
This seems to be a regular comment when talking about Rust (among other "incremental improvement" approaches) and I don't really get it: for 95% of the code things are simple enough that you don't have to think too much about it, the other 5% can either rely on one of the multiple-ownership safe abstractions or use unsafe. To me this framing sounds like "why do I need seat belts, if I fall of a cliff it won't do any good!"
> To me this framing sounds like "why do I need seat belts, if I fall of a cliff it won't do any good!"
I’m sorry that that’s how you read my comment — the languages I tend to use are much higher-level than Rust, like Haskell and Lisp, and I constantly found myself having to restructure my code to deal with memory-management. In particular, I found closures and futures difficult to work with — compared to a higher-level GC language. This experience may or may not be typical; I have few data-points to draw on after all!
Though I have found that the borrow checker often ends up pushing architectural changes on me as I'm fixing things.
This might be because I'm just newer with Rust, but I personally would find resolving 30 borrow-checker issues at once to be much more painful that resolving them as I go.
In C having to free allocated memory manually dictates how data structures are built and how your program flows. Often you have to keep track of extra state in order to free your memory at the right time and if you don't have this in mind from the get-go you're going to write code that leaks a lot of memory or doesn't work. The same is true for rust, but instead of the code leaking or not working it doesn't compile. If you turn those checks off with unsafe you're essentially stuck with an improved C that's just as difficult to prototype in.
Systems programming languages are simply not designed for fast prototyping, instead focusing on fast and in rust's case correct code. A GC, dynamic typing, high level library, etc. all help you prototype faster but you're not going to be able to use them to write an OS or a web browser.
Rust has very strict checks, and we -want- those. That's why we're using Rust in the first place.
I -could- use a more convenient / lenient language like ruby or golang but I generally wouldn't prefer it unless the use case made sense. You end up "paying" in different ways later down the line as your codebase grows in complexity.
In my experience, the rust compiler is extremely helpful and most issues I had went away after reading the rust book. [0]
[0]: https://doc.rust-lang.org/book/
The difference between Rust and Go, on the other hand, is mostly pragmatic: Rust is "strict" about design and implementation details Go's runtime handles for the programmer; there's nothing for Go to be "strict" about.
Rust is more rigorous than Go, but to a much smaller degree than Go is over Ruby.
Regarding memory management, sure, but Rust's strictness also handles things that Go doesn't. Two examples are error handling --- Result<T,E> in Rust ensures that you check for errors before using a result --- and thread safety --- the borrow checker prevents many synchronization errors that Go doesn't catch.
Rust gives a hard shove where Go gives something closer to a nudge, but in practice, Go's nudge (the requirement to do something with an error or else bear the shame of _'ing it) gets me 99.999% of the way to Result.
Rust has an `Error` trait too, and you can always just use `Box<dyn Error>` as your error type if you're fine with using a heap allocation to avoid caring about the concrete type (which is what's happening in Go when you use the error interface as your return type anyhow). Rust just makes the more efficient (in terms of runtime performance, not necessarily usability) the default and the more expensive option opt-in. This is a common pattern in a lot of the language design choices: performant by default, with the option to opt into a less performant variant to make things easier if you're okay with the cost. I think a lot of newcomers to the language (not saying you are one; I have no idea what your experience level is) don't realize this because the Rust community tends tends to _really_ like doing things the "proper" way. This isn't a problem per se, but I think there would be immense value in having more resources that made all of the options on the performance versus usability spectrum more clear.
One could argue that outside of safety guarantees that Rust provides it also happens to be a good language that people would like to use for those reasons (I really like the tooling, ecosystem and standard library when it comes to Rust, not so much the language itself).
Rust has many quality-of-life improvements that could be transplanted to a more relaxed GC-based language, but that won't be Rust any more. Here are some musings how such language could look like:
https://without.boats/blog/revisiting-a-smaller-rust/
(OT: why OP got downvoted so hard? It's a reasonable question to ask)
I also am disappointed that the OP got downvoted. Seems bad.
Ultimately I did get a solid explanation here. If I want a quick, fast and dirty language , Rust doesn't fit the role.
I really do love how C# gives me the ability to hack things together quickly, with still stopping me from doing things which simply won't work. Flutter takes this even a step further with the dynamic keyword( C# has dynamics too , but they aren't as robust ). I can write function calls with dynamics which is great for prototyping. Eventually bad things will happen if you use dynamics in every single call , but it helps me get started.
I definitely understand why many software developers see this as a dumbing down of software engineering, which I would attribute the downvotes to.
I would never write a large production app in Python though (having done it a couple of times). The flexibility becomes unwieldy at that scale and you have to have tests that exercise every corner or you'll have problems hours or days into running.
With Rust do you need less Unit testing ?
I find myself upvoting tons of comments in Rust threads where those comments are just asking questions, but are, for whatever reasons, downvoted. Happens on the Rust subreddit too. I don't know wtf it's about.
I too don't like rust evangelism around here. But any developer should know at least one system language, understand algorithm complexity, understand basics about memory management and caches, etc.. And probably it is easier to learn rust than C++ to those with web background.
With that stated, I would encourage you to understand why rust was designed this way and why the strictness of the compiler is actually a major advantage compared to languages like Python and JS where a lack of a type system actually makes the software much more prone to bugs.
There are also some immature crates for tracing garbage collection in Rust, like rust-gc, which typically has higher throughput than reference counting.
But as an aside, if you're just trying to prototype quickly, some useful "shortcuts" can be:
- Using `Box<dyn std::error::Error>` for functions which need to handle a result
- Using `unwrap` or `expect` to easily access Option internal values
- Using `unimplemented!()` if I'm trying to mock an API design. This one is nice because it will let you build out the signature including specifying a return type without having to build out the function logic.
Obviously these are shortcuts and not production suitable, but can be good to just get some code on the page that compiles
In retrospective, I make the life harder:
* Thinking like in the other langs I have learned (with exception of my first 2) is enough to just read fast some tutorial and start coding... I relent and pick a book to learn it right.
* Start worrying so much about performance, when never before I bothered much (what! this can ALLOCATE!). Now I clone like madman!
* Try to fit the OO mentality to Rust. Even in F# I don't fully commit to POCO objects and functions as I do in Rust now. Also, the lacks of polymorphism is what hurt me sometimes (ie: is harder in rust)
But now? Rust feel ABSURDLY productive. The thing I notice is how much I refactor stuff now, (even more than in F# that also was a up in this regard).
The "punishment" parts is only in my toy interpreter and very small now. For my main project ( a ERP/ecommerce app rewrite from F#) Is great!.
There's nothing preventing you from using multiple languages on your app. Say, Rust for foundations and Lua/Python/Scheme/Tcl/Whatever for glue.
A classic example of them being used in Rust is to be generic over arrays, like what the blog post talks about. Arrays encode their length as part of the type; [T; N] is the type of an array of Ts that’s N elements long. In other words:
How do you define ? to work on any length? When const generics is implemented, you’d write and now you can call this function on arrays of varying lengths.There's a bit more to it than that, but that's the core of it. Does that help?
Correct.
> How do you implement map, fold, etc., that work on an entire collection regardless of length?
So, while arrays exist, there are also things like slices and vectors, which have a runtime length, rather than an array's compile-time length. So that's one bit of it. But the other part is that these are implemented on iterators, rather than on the type directly, so usually what happens is "turn that into an iterator, then do map/fold/filter/whatever on the iterator. It is easy to convert the compile-time length array to a run-time length iterator, so it ends up being a uniform interface.
However, often times it entirely compiles away to nothing. What this may help with is bounds checks; it is possible for this to make it easier to remove/hoist bounds checks in some circumstances. In my understanding.
People wanting to use const generics themselves will want to follow this issue, which is about stabilizing a minimally-useful subset of const generics: https://github.com/rust-lang/rust/issues/74878 . It seems to be coming along well: "Regarding the remaining steps: there are only a handful of issues I think are blocking min_const_generics at this stage (several of which already have open pull requests). We also want to add many more tests, which is something anyone can easily help with. If you want to tackle one of the issues, feel free to ask @lcnr or myself how to get started; I'm going to dedicate time to fixing whichever remain."
I did an experiment with units of measure years ago in Rust: https://github.com/coder543/metric
To me, it felt ergonomic, and it was absolutely compile-time type safe to the extent you would expect. It worked just fine. I know of quite a few other libraries that do the same thing with their own unique approaches, so there are many flavors of this that all work on stable Rust.
If you're asking for something like this to be built into the standard library... that's very unlikely to happen in the foreseeable future.
most numbers i deal with in my code have units somewhere. mistakes happen. i don't want to choose from multiple unique approaches, i need the ecosystem to standardize on one solution. see also the async task executor problem in other languages.
But, yes, you probably could transmute it... if you really have to.
The reality is that the conversion cost to just `map` one Vec to another is extremely low on anything but maybe a microcontroller —- and even then, maybe not.
If you benchmark things and can’t find a safe way to do things, there are unsafe methods, but I am highly skeptical that they would be needed.
It’s also extremely possible to fork such a library, change a few lines to use the dimensionally correct type, and be done with the issue.
If all of your heavy lifting is being done by this external library anyways, and that library just uses raw floats... why bother with the ceremony of using a dimensionally safe float in your own code if the performance cost of mapping between types is too high?
The real benefit of dimensional safety is when you’re actually doing computations, not some external library you pulled in.
Rust allows for these zero cost wrapper types that will do exactly the correct computations as if they were done with hand written code... but you have to actually have computations that you want to do. The type system intentionally doesn’t want you to treat these typed floats as regular floats. That’s the point.
But there are ways to transmute certain types for no performance penalty, they’re just footguns you should avoid except as an option of last resort.
Every audio library I've seen so far operates on (*i16/f32, size_t) or &[i16/f32]. An audio library which supports dimensioned values is hypothetical. And most dimensioning libraries don't have a unit of "sample" (in time) or "audio amplitude" or "channel", so the audio library won't even know what unit system to pick.
> It’s also extremely possible to fork such a library, change a few lines to use the dimensionally correct type, and be done with the issue.
And suddenly the audio library is tied to your specific unit system, and you still need transmutes or whatnot when talking to the OS sound library.
> If all of your heavy lifting is being done by this external library anyways, and that library just uses raw floats... why bother with the ceremony of using a dimensionally safe float in your own code if the performance cost of mapping between types is too high?
Most of the time I end up just using a named type alias, instead of a strong newtype.
I gather from the description that this is not enabled by default. Is that correct? If so, why isn't it enabled by default? It would seem to fit with Rust's focus on safety.
In general, we update regularly to gain access to bug fixes and because upgrading regularly makes each upgrade easier, so we don't need something specific to trigger an update.
> Shorter backtraces
This is a very welcome change! Having to find the "root" of a full stack trace can easily take 30 seconds, which really starts adding up.
All the core tools I tested works 100% and almost all the crates work. A few, like libc and Nix, still needs explicit riscv64 support.