4 ms·
Coming from C++, it didn't take months. I got a couple lifetime errors at the start, but I understood the reason behind them all[1]. Now I don't get the errors[
by yakubin 4y ago
Coming from C++, it didn't take months. I got a couple lifetime errors at the start, but I understood the reason behind them all[1]. Now I don't get the errors[2]. In C++ you need to think about the lifetimes anyway or your code will break at runtime, so then coming to Rust it's not that much of a problem IMO.
[1]: Except the errors about partial (non-aliasing) borrows of struct fields. That was annoying, although circumventable with some boilerplate. Luckily, it's solved now in the compiler.
[2]: Although it may be to a large degree thanks to using arenas for allocation, which simplify lifetimes a lot by grouping them into large buckets instead of tracking each little object separately with its own little lifetime.
- CraigJPerry 4y ago>> it may be to a large degree thanks to using arenas for allocation If you're going to do that, would you not be better saving all that time and code bloat (it's code you wrote that doesn't contribute to solving your problem) by just using a garbage collector from the outset? This propensity to fall back to arenas and the other puzzling behaviour i observe is liberal use of "#[derive(Copy, Clone)]" - these seem counter productive. Why are people wasting time only to reinvent solved problems? Or to frame it more charitably, rust is allegedly a super-power for developers. Why are rust devs not pushing the boundaries of what it means to be a developer? We do we not see very large code bases on github maintained by a small number of devs? Allegedly the combination of static typing + borrow checker makes code maintainability a breeze. So where's the evidence?
- yakubin 4y ago> If you're going to do that, would you not be better saving all that time and code bloat (it's code you wrote that doesn't contribute to solving your problem) by just using a garbage collector from the outset? I suspect a garbage collector would be pretty nice. However, I can't just list the features I want and get a language (unless I make it myself, which would take a lot of time). Currently, my imaginary perfect language would actually have a garbage collector, would be pretty similar to Standard ML, but would be more focused on arrays than lists, and would have value types --- in Standard ML I can't have a value array of records (structs in Rust/C++ parlance) --- they will be behind a pointer. And if I were to stick to the standard or make sure that my code compiles with other compilers than MLTon, then I can't even have a value array of 64-bit words, although I can have a value array of 63-bit words. This one bit may seem insignificant, but for certain algorithms it's a big complication. Powers of 2 simplify a lot of algorithms (and are "faster" to be a bit loose with the language). There are other features I'd like, but already this short list makes for a currently-non-existent language. OCaml and Haskell have similar problems to Standard ML. At the same time Rust has great support for arrays, is expression-oriented, has sum types. Value types are the default. It generally ticks a lot of boxes I care about. I can't just go and say "now give me all that but with a GC" and have it appear before me. Also, arenas I use are linked to logical portions of my programs. They are not contrivances that I had to think long and hard about. They don't waste my time really. I've spent 0 time thinking about how they should be organized. Now the part where a GC would be helpful is a bit of a more liberal use of closures, and eliminating code noise coming from lifetime annotations such as "for<'a>". But I can live with the current state of affairs, if I get all the other benefits. > If you're going to do that, would you not be better saving all that time and code bloat (it's code you wrote that doesn't contribute to solving your problem) by just using a garbage collector from the outset? If anything, Rust is an asset for large teams of devs. Even though you may sometimes argue that a handful of C/C++ devs can keep their whole project in their heads and not make mistakes (although I think that's a stretch), the moment you get a large C/C++ team, weird hard-to-debug bugs coming from memory- and thread-safety issues start to creep in. There are other high-level languages, but Rust is the one with a combination of performance competitive with C++ and large ecosystem of libraries you can use. Examples of Rust projects with a large number of contributors facilitated by the language taking the fear of intractable bugs away: - <https://github.com/BurntSushi/ripgrep https://github.com/BurntSushi/ripgrep> - <https://github.com/clap-rs/clap https://github.com/clap-rs/clap> - <https://github.com/rayon-rs/rayon https://github.com/rayon-rs/rayon> - <https://github.com/cloudflare/wrangler https://github.com/cloudflare/wrangler> - <https://github.com/sharkdp/fd https://github.com/sharkdp/fd>
- CraigJPerry 4y ago>> Currently, my imaginary perfect language None of us are getting a pony but that's not the point. The point is about selecting the right tool for the job. For example, I see projects like axum[1] and wonder what people are using it for. In a PHP or Rails or Django stack, this is your nginx / apache layer. It's not business value code, it's the fast plumbing under your stack. What are people adding to the fast underlying plumbing layer that's giving them a competitive advantage? Or is it just cargo culting? >> If anything, Rust is an asset for large teams of devs I mean i only asked for an example of a small team performing magic thanks to rust so if there's no examples of that, i can be certain there's no examples of a large team performing magic thanks to rust! [1] - https://github.com/tokio-rs/axum https://github.com/tokio-rs/axum
- yakubin 4y agoI don't write web services, so it's hard for me to answer about axum, but if I were to speculate, I'd point out that PHP, Ruby and Python have good integrations with tools like Apache and nginx, while AOT compiled languages like C++ and Rust do not. There is CGI/FastCGI, but then you're parsing strings in true Unix fashion. When instead you use a web server library written in the language you use, you get to use a richer interface than functions [byte] -> [byte]. Go is nicer here since it has that in the standard library, but the idea is the same. But again: take that with a grain of salt --- I don't write web services. > I mean i only asked for an example of a small team performing magic thanks to rust so if there's no examples of that, i can be certain there's no examples of a large team performing magic thanks to rust! Well then, my examples of large teams should be sufficient. :) Oh, and I've just remembered about this nice project: <https://github.com/firecracker-microvm/firecracker/ https://github.com/firecracker-microvm/firecracker/>
- adwn 4y ago> If you're going to do that, would you not be better saving all that time and code bloat (it's code you wrote that doesn't contribute to solving your problem) by just using a garbage collector from the outset? An arena-based allocator will run circles round GC and malloc: no need for tracing or bookkeeping, just one allocation and one deallocation for any number of objects created (or, at least, the ratio of created objects to memory allocations is very high). And with Rust, the compiler will give you an error message if you try to hold on to an object in an arena you're trying to deallocate.
- CraigJPerry 4y ago>> An arena-based allocator will run circles round GC and malloc Where does the overhead of copying the surviving objects over to the new arena as you drop the old one factor into this? Or maybe you could own more non-problem-solving code and permit cycling of one arena? Could get tricky if you want to rely on destructors? The ratio of lines of code written to solve the problem vs written to shave yaks is getting lower still here.
- adwn 4y agoYou're misunderstanding the purpose of an arena allocator: It's only for relatively short-lived objects which are all released at once; there's (typically) no transfer of objects to a new arena. This isn't as uncommon as it might seem – for example, auxilliary data which is created during the execution of an algorithm, and which isn't needed anymore once the algorithm has finished, or per-frame data in a computer game. > Or maybe you could own more non-problem-solving code [...] You know, sometimes performance is the problem, and code which improves performance does solve that problem.
- IshKebab 4y ago> Except the errors about partial (non-aliasing) borrows of struct fields. That was annoying, although circumventable with some boilerplate. Luckily, it's solved now in the compiler. Only within a function. You still can't partially borrow a struct across function boundaries. Anyway I don't believe you that you only got "a couple of lifetime errors at the start, but I understood the reason behind them all". Either you're bullshitting or you have restricted yourself to extremely simple Rust code and haven't really realised how complex them can be (e.g. what does `for<'a>` and `+ '_` mean?) > In C++ you need to think about the lifetimes anyway or your code will break at runtime, so then coming to Rust it's not that much of a problem IMO. C++ definitely gives you a head start when thinking about lifetimes, but there's still a huge difference between thinking about lifetimes, and writing them down to prove to a not-especially-smart borrow checker that they're ok. Obviously the latter is better, because it gives you guarantees. But it's still way way harder.
- GrumpySloth 4y ago> Anyway I don't believe you [...] Either you're bullshitting or you have restricted yourself to extremely simple Rust code How kind. > what does `for<'a>` and `+ '_` mean? `for<'a>` allows you to introduce a new lifetime parameter which parametrizes an Fn argument independent of the lifetime parameters of the enclosing function/struct. Sounds complicated when you try to explain it in programming terms, but actually when you think about it in terms of logical quantifiers from maths (for<'a> being the universal quantifier introducing the 'a "variable", "for" -> "for all"), it's pretty simple. The idea is simple, the syntax is noisy. I've already complained about the noise in another comment: <https://news.ycombinator.com/item?id=31433427 https://news.ycombinator.com/item?id=31433427> "+" in type constraints is just conjunction. > C++ definitely gives you a head start when thinking about lifetimes, but there's still a huge difference between thinking about lifetimes, and writing them down to prove to a not-especially-smart borrow checker that they're ok. Obviously the latter is better, because it gives you guarantees. But it's still way way harder. Getting a program to compile may be harder. But the whole task of writing a program long-term gets easier. Most of the complaints about borrow-checking I see online can be split into two categories: 1. syntax noise --- I agree 2. this program would work, if not for the borrow checker --- in almost all cases here people do not think about API boundaries at all. They look at the body of the functions they wrote and conclude that it should work. But they forget about one important thing: the compiler needs to ensure that changing the body of function f() without changing its signature should not break compilation of function g() which calls f(). That leads to examples where it seems as if something should compile, but doesn't, because of function boundaries which mandate that lifetimes should be correct regardless of function implementations. And there is a good reason for that: if I use a library, this library pushes out an update which doesn't change the types of anything, I don't want to be forced to update my code for it just to compile. So the work that ensures that everything stays composable throughout implementation detail changes is moved upfront. The alternatives to "fighting the borrow-checker" are GC or difficult to debug (sometimes even to reproduce) bugs. Fixing the compilation is a lot easier for me than tracking down memory-corruption bugs I face in large C++ codebases with hundreds of contributors, so I disagree with the claim that making things work in Rust is harder.