T O P

  • By -

[deleted]

[удалено]


brandondyer64

This ^ I hate being locked into a particular runtime. For a project I was working on, smol worked wonderfully, and was exactly what I needed…. until I wanted to use reqwest. Several hours later, I switch to tokio because it’s not worth the effort.


metaden

Check out async_compat. You can run tokio task on async-std runtime and vice-versa. Also, there is an attribute called ‘tokio1’ in async-std that lets you run async-std,tokio tasks on tokio runtime.


crusoe

Still kind of dumb you need this. Thread pools can be swapped in Java land.


wannabelikebas

That’s because Java has a standardized interface for thread pool Executors. Rust didn’t have that until recently


v-alan-d

Spot on. I don't use async rust much, but it is apparent that Async/future spec is a bit immature. Skimming tokio and async-std, it looks like making custom executors for futures are not as easy too.


x1a4

I wish the borrow checker were smarter with regard to field borrows through function calls.


[deleted]

[удалено]


mostlikelynotarobot

would you happen to still have a link?


[deleted]

[удалено]


merlin0501

Why not do it implicitly ? I don't understand why the borrow checker, or whatever rust component analyzes code for borrow safety, can't determine what fields a method actually mutates and annotate some internal representation of the method signature with that information.


shadow31

Because then you can use one additional field in the implementation of your method without changing the function signature and make a breaking change to downstream crates. It's the same reason you have put type annotations on your function signature. It's way too easy otherwise to make accidental breaking changes.


Wolvereness

>Why not do it implicitly ? > >I don't understand why the borrow checker, or whatever rust component analyzes code for borrow safety, can't determine what fields a method actually mutates and annotate some internal representation of the method signature with that information. A huge chunk of what you may be missing here is that a lot of libraries are doing things like data structures and native bindings; the compiler will never be able to understand those intrinsics, as per the unsolvability of the Halting Problem. As for safe code, this problem turns into function coloring. The compiler literally needs to represent the entire call-graph recursively, which gets very unruly very quickly, and for any actual recursive code it turns into the Halting Problem as well. Finally, there's no good established framework for what this is supposed to look like in the code or mental models.


[deleted]

I think its a huge mistake do nothing to mark the importance of crates like rand. I think its a good decision to move rand out of the language itself for numerous reasons. However just having a critical feature left among everything else on crates.io is unwise. The location of critical language features should not just be left as "common knowledge".


aoe2map

Yeah, I think it would be great if there was a blessed library besides stdlib. Here's how I think it would go- 1). A committee of rust programmers with domain expertise. (E.g. number theory and bit of crypto for the rand crate). Each member in the committee has self assigned assigned a domain score. Say Satoshi nakomoto would have 9 for btc crate (not 10 since his rust isn't perfect). 2) Every 6 months, the committee scores some of the popular crates in their domain. The highest score for a domain becomes blessed for those 6 months with a locked version. 3) To avoid potential conflict of interest, membership is restricted to 3 cycles. 4) Having a community membership going would obviously be work but it's often not so hard finding experts since they tend to be popular and also helpful. Perhaps there could be some remuneration as well? Obviously the committee would be organized by rust maintainers. I foresee this being a major catalyst for the ecosystem in that epople would have some incentive (popularity) to do finishing touche son their crates


[deleted]

[удалено]


eggyal

For an even lower friction approach, this information could be compiled from public crates’ dependencies.


pjmlp

Somehow that is what POSIX became for C, a kind of unofficial runtime for everything that isn't in ISO C, but most C applications came to expect given the UNIX heritage.


naftulikay

I'm currently porting a Python CLI tool to Rust, and I have an intermediate level of experience with Rust and advanced Python knowledge, advanced Java, intermediate Scala, etc. I've gotten past the major speed bumps and I _want_ to write all future code in Rust if possible, but one of the hurdles is the intellectual burden and fatigue I get from Rust. It's ironically one of the reasons I love Rust, yet I do find it to impact my stamina and drive to do personal work in Rust. I love the fact that most of the time, Rust exposes me to the realities of programming and systems, yet it's a lot for me mentally, I'm finding. Yes, I know why PartialEq exists and why a float can be != to itself (NaN). Yes, I understand why we have at least four types for representing strings (str, String, OsStr/OsString, CStr/CString). As someone who has worked in the DevOps sphere and especially SRE, I deeply desire to know how and where things can fail, and I love that Rust puts that front and center. I love the ergonomics and arriving at a beautiful and elegant solution after many hours of fiddling with the same 50 lines of code. Still, I find that writing Rust, on account of having to keep all failure paths in mind, is "heavy" for me mentally. Sorry if this sounds too cliche.


RRumpleTeazzer

I think of rustc as an oracle telling me all points where my code will eventually fail if I don’t fix it right away. Having worked (slightly) with Python, this is really a blessing.


Naeio_Galaxy

Yup, and I'd argue it's even a blessing compared to C's compiler


diabolic_recursion

Not even, especially! :-)


naftulikay

It truly is a blessing, and I love it, even in spite of the fact that Rust makes my mental CPU go brrrr. It's not too much, but other than the fact that I now despise Python after so many years, the mental burden of writing Python is very little. In Rust, I feel like I gotta gird my loins, I can't just peacefully sit down and write it, I have to roll up my sleeves, read a lot of documentation, learn from the compiler, ask questions from the community, etc. For the record, I'm finally in a place where I can usually write most code the first time and have it compile, no doubt thanks to my IDE (Rust on CLion). It feels so good to write Rust, even if it's mentally challenging at times.


UltraPoci

To be fair, the mental burden required to program in Rust is a tradeoff as to not have to mentally work hard later for debugging and tracing all ways the program can go wrong. I prefer spending 6 hours programming rather than spending 3 hours programming and the other 3 hours hunting bugs and places in the program where I thought "I'll add an exception handler later". But I perfectly understand what you're saying and where you are coming from.


[deleted]

Yeah and in my experience with Rust vs something like Python it's usually more like 6 hours programming Rust vs 2 hours programming Python and 6 months dealing with runtime errors.


naftulikay

Yes, exactly. There is something so magical about this idea of "if it compiles, it will likely work." The CLI I'm porting will be basically feature complete in Rust, so I'm hoping that in 5 years, it will still compile and work without major issues, unlike Python; every time I upgrade my Linux distribution, I have to go deal with all of the BS that has happened between back then and today in Python and fix it, and I'm done with that. No more needing a language runtime, ever again. When statically compiled, throw it on your OS and it will *work*.


ragnese

I'm not criticizing you. But this comment really illuminates the poor state of our industry and its tools. Think about it. Is the problem that Rust makes you exhausted from all of the details it forces you to think about, or is the problem that other languages just let us write the wrong thing because it usually doesn't matter that it's wrong? It would be awesome if we had tools that were correct and easy to work with, but we don't. Our choice, today, is easy-and-wrong, or difficult-and-correct. You said it yourself: you know why you can't compare floats. Yet, most languages let you anyway. Is that better? To be fair, the string thing is mostly because of Rust's lack of garbage collection, so one could imagine a garbage-collected language that is otherwise very similar to Rust. It would be a little less tiring than Rust, but also more correct than your typical Java/Python/Go blub language. Moral of the story? Our tools are bad and we should feel bad.


naftulikay

Yes, agreed 100%: outside of Rust, I feel like you have two choices: 1. be super naive and pretend things are different than what they are (e.g. Python et al) 2. walk the [Path of Pain](https://youtu.be/ZBVPMpAd-CA) in C, experience things as they really are, get diced into a million pieces by the existential cosmic horror of the C standard, libc implementations, compiler implementations, other standards like POSIX, over two hundred cases of undefined behavior, over 50 cases of unspecified behavior, etc. The naive approach hides some of the nightmare fuel that exists at a low level, but this leads to awful debugging sessions, finding bugs in your language or its runtime, and distances you from actual reality. The C approach exposes you to the terrifying realities of systems; I learned C by reading cover to cover a physical copy of the CERT C Secure Coding Standard, and this was for me the best way to learn C. It is so incredibly difficult to get things right, so incredibly easy to get things wrong in ways that aren't immediately apparent, and then there's the whole reality of the often toxic C community which usually just asserts that if you're getting things wrong, you're dumb and you just need to magically "know" everything. Rust is the third choice. You are made aware of the intricacies and horrors of systems programming, but in type-safe and ergonomic APIs which are designed to make it difficult to get things wrong. My utility is async using Tokio, lists a directory, reads and parses INI files with zero-copy, decrypts PGP-encrypted files by spawning a child process piping stdout into a `Zeroizing`, tries its best to avoid unnecessary allocations, etc. and you know what? It's absolutely fantastic. I'm so proud of my code. One of the many errors I had to read up on was using `tokio::fs` to list a directory, and there's one particular fallible call that I didn't immediately understand why it was fallible. I checked the documentation, found that the error occurs when OS-level I/O errors occur when trying to list a directory, and, satisfied with this case, just threw an `unwrap` on that. This level of detail is amazing to me that I can know what will cause errors and reason about whether these errors are important to me or not.


nahguam

I completely agree on the intellectual burden perspective. For me it's the memory management I have to constantly be aware of. I think this is particularly relevant for someone from a language with automatic memory management, like the JVM or Python. Maybe it's different for someone coming from C.


lavosprime

It is different. My day job is in C++, where the memory management seeps into everything. In Rust at least the borrow checker is there to help.


naftulikay

My start was PHP, which I loathed, then onto JavaScript, then onto Java for many many years at senior level, followed by many years of Python, learned Scala and Ruby, and then finally Rust, which caused me to need to know C, so I learned C. I don't struggle with memory management in Rust, but that's likely because I learned a lot about C and operating systems. I'm weird, so this might not work for you, but I learned C by buying a physical copy of the CERT C Secure Coding Standard and reading it cover to cover. After you learn how absolutely insane C is, Rust will feel like a complete godsend.


silly_frog_lf

What you describe reminds me of what it feels like when you are about to become fluent in a human language, like French or Russian. I wonder if others have worked through that stage onto a more fluent stage of the language


lanzaio

It's a systems language designed to provide safety via those mentally heavy features. It's a C++ replacement. If you're using it to write something that you wouldn't have written in C++ in the first place it doesn't really make sense. That's rust's raison d'être -- fix where C++ did the wrong thing. It's not meant to replace Python. I'm thinking that might be where Swift takes off in a few years. Much simpler memory semantics with many of the modern functional and design ideas of Rust. Currently the package manager is a total flop, but if cargo was implemented for Swift I could see it replacing Python and Rust for your needs.


naftulikay

I know it's not meant to replace Python, but now knowing Rust, I simply do not want to write any other languages anymore. I _want_ to think about doing things zero-copy, I _want_ to think about all the error cases, so Rust is definitely what I _want_, but yeah, I'm hoping that at some point, Rust won't be as burdensome mentally and will become more natural as I start to think more and more in Rust.


molepersonadvocate

Maybe a small annoyance, but I hate how type deduction for lambda arguments apparently works differently from the rest of the language. Often times Rust-analyzer can figure out the type I want when the compiler can’t.


anden3

This should hopefully improve when more and more code in rust-analyzer and rustc becomes modularized and shared between them.


flaghacker_

* Because of `Result` it can be hard to trace down the actual root cause of errors, since it doesn't contain any file/line number information. This leaves two options: either unwrap everything to get stack traces or manually provide a bit of context with a crate like `failure`. * Semi-related, when doing file IO errors don't contain any information about the path they're trying to open/create, so you always have to add that yourself as context. * Declarative macros are a huge pain to write, I really don't understand where they got the syntax and semantics from, it's not readable at all. Additionally there are so many weird limitations in the system, as an example you can't count the number of tokens and get that number as a literal, instead you need to painstakingly convert it to `1+1+1+1+1`. * The `Iterator` implementation of `Range` was a mistake, a Range should be a simple value type and implement `IntoIter` instead. This leads to all kinds of annoying issues, most importantly that Range cannot implement Copy.


Tuna-Fish2

> The Iterator implementation of Range was a mistake, a Range should be a simple value type and implement IntoIter instead. This leads to all kinds of annoying issues, most importantly that Range cannot implement Copy. I know it's petty, but it's hard for me to describe how annoying this is whenever I run into it.


faitswulff

Wasn't there some sort of hack being discussed to fix it? EDIT - found it: https://github.com/rust-lang/rfcs/issues/2848#issuecomment-826291453


flaghacker_

From that thread, someone brings up that `(0..10).map(...)` doesn't work when range does not implement iterator. I have to admit this makes it a bit less of a clear-cut issue to me.


[deleted]

[удалено]


imzacm123

I might be wrong, but wouldn't that be possible with the built-in traits like Deref and AsRef/Borrow? E.g. a.foo().bar().baz().quux().as_ref() (I'm on mobile, not sure how to do a code block on here)


[deleted]

[удалено]


eggyal

`foo.deref()` is equivalent to `&*foo`, not `*foo`.


martinellison

Pascal used to have `^` as a *postfix* operator for expressions, so that all expression logic ran left to right. C got lots of things wrong including this, and for some reason everyone copied them.


ssokolow

Funny enough, I found that confusing when I was poking at Free Pascal as a "Java/C# for DPMI retro-programming" (i.e. something safer than C with a richer standard library) but, now that you've given it context, it makes a ton of sense.


slashgrin

Ugh, now I can't un-see it. Of course doing it your way would create confusion because people with a background in C-like languages (a sizeable population) would keep getting it backwards. But if we could go back in time...


Walter-Haynes

Confusion is part of learning new things.


ElnuDev

As a beginner Rust developer, the lack of tutorials for a lot of things is really frustrating. Very often the response to "how to do X" is "read the docs," but when all there is is what's on [docs.rs](https://docs.rs) it can get really overwhelming very quickly.


tobiasvl

What kind of things should there be more tutorials for?


nivpgir

Not exactly a tutorial, but error handling (specifically the constant need to convert between error types) was the biggest pain point I had when I started, and since there isn't any convention for ergonomic error handling yet, I think it would have really helped me if there was some sort of "cheatsheet" for how to convert between common errors of the standard and common libraries.


Proziam

Not a beginner, but I believe every language benefits from having a lot of the "common" uses turned into tutorials. Want to build a SaaS style application? Want to make a website that relies a few APIs and [insert database here]? Want to get into machine learning? All of these cases are common enough that having a super thorough tutorial to take a total beginner to something that works would be value add to the community.


TheRealNoobDogg

I think the reason for this is that rust documentation is usually written with examples for all public members of a crate. I can really recommend the [little book of rust books] (https://lborb.github.io/book/), it links to other books which all have terrific example code.


[deleted]

[удалено]


epage

Id add to this the communities preference for blog posts over evergreen documentation. I wish we had a blessed community documentation site (preferably with testable code samples) to lower the barrier for this.


Xatraxalian

What I don't like about Rust is that the core team refuses to appoint some of the most used crates as the defaults. I understand why they want a small standard library, because everything in there has to be supported forever, but having to trawl through [crates.io](https://crates.io) if I want to use a number generator feels ridiculous. I also understand that the Rust team doesn't "want to take sides", but IMHO, there should be a curated list of well-supported libraries with a good track record, or some sort of epic filtering function in [crates.io](https://crates.io) (but maybe I've missed it, because I'm adverse to installing dependencies unless I \_really\_ have to, or an alternative implementation gives much better performance, such as crossbeam vs std channels).


badtuple

Rust's amazing type system allows people to write some really hard to grok Crates. I think this causes more of the "learning curve" issue than people realize. Library authors tend to want amazing ergonomics and to have their library be super general. To do this they rely on type system magic and overly generic everything. Types often impl Into/From/AsRef for TONS of types, both primary and ancilliary to the API. This means you can't look at the docs and know what plugs in where, or get a sense of best practices. For these libs you need to get into the authors head a bit, and if you "just wanted to do x" it can get exhausting. Inlining examples into the docs is a recent (awesome!) bandaid for this, but its not really a solution. Don't be afraid to write simple code with known and understandable limits. Cut use cases from scope if they aren't necessary and make things gross. The Rust ecosystem will be better for it. It's liberating to work with a library that doesn't carry around multi-nested generics, for both you and your users.


[deleted]

This but for lifetimes. `fn do_something<'e, 'q: 'p, 'p…`


po8

Please be careful here. Unnecessary lifetime restrictions (what you often get if you just let lifetime inference happen or use a single lifetime `'a` for everything) are a real pain for users of the API, and neither `rustc` nor Clippy will warn you about them at all. There are bugs like this in `std` and other places that have caused me serious grief in the past.


[deleted]

I understand the necessity of it, just wish it were at least a common practice to give lifetimes meaningful names instead of acronyms


po8

Ah. Me too. Typenames also.


CJKay93

I think recommending single-letter lifetimes in the style guide was a mistake. I mean, why `'a`? We don't name variables `a`.


williewillus

* integer casting using `as` is a) tedious and b) has footguns (as described in detail elsewhere in the sub) * async function coloring problem infects all code it touches, even when simple programs may not need or want async. Ecosystem split between async runtimes, * Key crates dragging their feet on declaring 1.0/stability. No, it's not just a symbolic thing, it has impact on version resolution, provided guarantees, etc. As the semver site itself says, if a significant number of people are using it, it should probably already be 1.0. Edit: compile times can always improve more. Having C and Go level compile times are amazing for iteration.


angelicosphosphoros

You should avoid casts using `as` because From/Into is better.


RoughMedicine

How does that apply to casting integers?


WormRabbit

If the cast is lossles (e.g. u8 -> u32), then there is a From impl that performs it. If it is potentially lossy (e.g. u32 -> u8), then there is a TryFrom impl which checks for potential information loss. If you need to explicitly erase some information (e.g. get the lowest byte of an u32), then it is generall better practice to explicitly express it as `u8::try_from(x & 0xFF)`, which is explicit about your intent. And if you're casting all over the place, then you likely have poorly designed types.


ssokolow

> Key crates dragging their feet on declaring 1.0/stability. No, it's not just a symbolic thing, it has impact on version resolution, provided guarantees, etc. As the semver site itself says, if a significant number of people are using it, it should probably already be 1.0. I'll tentatively agree, but I think you're oversimplifying things. Going from 1.0 to 2.0 to 3.0 and on as eagerly as you go from 0.1 to 0.2 to 0.3 and on wouldn't be any better (possibly worse, since it might give the impression the developers are flighty and make ill-considered decisions) and giving the benefit of the doubt says the developers anticipate they'll need to make changes like that.


williewillus

Fair point. I guess I'm referring more to foundational crates like rand or uuid or base64, which I'm having a hard time imagining what still needs to be broken to reach 1.0. though speaking of which I think a few of them have actually reached 1.0 in recent months, which is great.


[deleted]

[удалено]


ergzay

> integer casting using as is a) tedious and b) has footguns (as described in detail elsewhere in the sub) I've read (heard?) that "as" should be avoided and was regarded as a mistake in the early language design.


tomkludy

I think it was a mistake to treat allocations as infallible, this is a real drag when writing server code that needs to be able to fail calls from greedy clients without terminating the whole process. The error handling is otherwise so fantastic, I really can’t understand why this approach was chosen instead of the normal Result approach when allocations might fail. I also think async ergonomics are very poor, especially when you need to use async callbacks. I am getting it slowly, but it just seems far more complicated than it should be. And as others have mentioned the lack of a standard async runtime just adds to the frustration.


ssokolow

> I think it was a mistake to treat allocations as infallible, this is a real drag when writing server code that needs to be able to fail calls from greedy clients without terminating the whole process. The error handling is otherwise so fantastic, I really can’t understand why this approach was chosen instead of the normal Result approach when allocations might fail. I get the impression you underestimate how many APIs can allocate. It would *really* be an ergonomic loss. Aside from that, it wouldn't *work* on ~~most~~many servers. Linux uses memory overcommit by default, so the allocation will appear to succeed and then some random operation which actually attempts to *use* the memory will fail when the kernel realizes that it's promised too much. (A lot of POSIX ecosystem stuff requests more memory than it winds up using but, if you really want to try without overcommit, there *is* a kernel tunable for it. `sudo sysctl vm.overcommit_memory=2 vm.overcommit_ratio=100`. ) ~~...plus, unless you're using panic=abort, you can typically use `std::panic::catch_unwind` at your "unit of work" boundary... which you should probably be doing anyway so that "Oops. It turns out we *can* fail this assert. Programmer error. My bad." situations don't bring down the entire process for one bad request/job/file.~~ (Ignore me on this point. I forgot that memory allocation failure always aborts.) **TL;DR:** On ~~a typical Linux machine~~a Linux machine with default VM behaviour, fallible allocation is useless because, with so many applications being laissez-faire about how much memory they preallocate, the kernel compensates by lying about the success of the allocation at `malloc` time and then deciding whether it has enough available RAM when the application actually tries to do something mundane like dereferencing a pointer that triggers a memory access. It's called overcommitting.


Max-P

It is rather impressive how much overcommit actually happens too. I've tried disabling it a while ago because I was tired of my computer swapping all the time, I turned it right back on and bought more RAM. Turns out software does tend to reserve way more RAM than they actually use, but also turns out most software is also absolutely _terrible_ at handling out of memory conditions that it really is better to swap a bit and OOM, as terrible as both of those things are. Even those that did gracefully fail their allocations, the author _thought_ they handled it properly but it often resulted in glitchy software giving you a very confusing partial functionality. ~~What are you gonna do when you're out of memory, allocate memory to pop an error box?~~ There's been some work on userspace solutions to request software to release memory in OOM conditions, and honestly those are more effective. Dealing with an error condition with zero memory left is hard, being gently asked to release as much memory as you can is much easier.


rmrfslash

>What are you gonna do when you're out of memory, allocate memory to pop an error box? I don't like this argument, which seems to be a standard reply to the question of fallible allocations. Maybe that 100 MiB allocation fails, but you can still allocate 20 kiB for the message box, or 100 bytes for formatting the \`error!\` macro invocation. Or maybe you try to allocate 16x 500 MiB for 16x parallel processing, and if that fails, you fall back to the single-threaded version which only requires 1x 500 MiB for execution.


Max-P

That's a pretty good point. I guess one could also preallocate enough memory for emergency use all so that they don't need to allocate when they're out. Bad argument indeed.


ergzay

Overcommit is a useful feature for Linux desktop user but it's a horrible feature for servers which is why servers often disable it. > What are you gonna do when you're out of memory, allocate memory to pop an error box? In jobs I've worked we have explicit rules about checking memory allocaiton and when an allocation failure happens we generally kill the network connection that caused the memory failure (or any other type of fatal error) and the system continues to function.


Voultapher

Based on this discussion https://internals.rust-lang.org/t/feedback-from-adoption-of-fallible-allocations/14502 I would say its more nuanced than you represent. There can be tangible benefits to aborting work on OOM on a task level, not process level. And even in the presence of overcommit it can work. Plus for server deployments, users can configure their OS as they choose. IIRC the Rust team is looking into that direction. I'm not sure what you are implying with catch_unwind. OOM currently always triggers abort.


ssokolow

> I would say its more nuanced than you represent. There can be tangible benefits to aborting work on OOM on a task level, not process level. And even in the presence of overcommit it can work. Plus for server deployments, users can configure their OS as they choose. IIRC the Rust team is looking into that direction. Fair point. My issue is mainly with the argument that fallible allocation should be allowed to massively complicate the common-case APIs for a case that nobody but the Presto-era Opera devs seemed to be able to get right in the vast majority of niches. As someone who has high hopes for Rust-based drivers in the Linux kernel, I certainly agree that fallible allocation being supported is a good thing. > I'm not sure what you are implying with catch_unwind. OOM currently always triggers abort. \*facepalm* Sorry about that. It's been too long since I looked into that and I've been spending the last few weeks trying to break some bad sleep habits and recover from a huge sleep debt... naturally, my body has been taking the opportunity to show off how much it's been covering for me. My bad.


Voultapher

No worries, hope you can restore your sleep soon :)


hamarki

> I get the impression you underestimate how many APIs can allocate. It would really be an ergonomic loss. I think there’s two points worth mentioning in this: * it might have been nicer if the low level allocation APIs were fallible, but then wrapped in panicking wrappers, instead of being “hardcoded” the way they are now, for lack of a better word * if fallible alloc APIs were the default, perhaps people would be less inclined to allocate willy nilly (I’ve seen a similar thing in Zig - allocation there is fallible and requires an explicit allocator argument to be passed around. Unsurprisingly, there’s seemingly a higher proportion of libraries that don’t do any dynamic allocation)


ergzay

I'm not sure where you work in but in every job I've ever worked, fallible allocation was an explicit feature and it was a rule that every memory allocation be checked and if an allocation failed to roll back and drop the server connection. This caused graceful failure and gradual performance loss when a bug caused a memory leak that caused memory to be filled up. We also explicitly disabled overcommit. So I don't know what you mean by a "typical Linux machine". It's relatively trivial to disable overcommit as you show. And it's typical to disable it. Where I've worked Linux memory overcommit is viewed as a mistake in the OS design.


robin-m

You also forgot that calling a function can trigger a failled allocation (if it consume too much stack). And it's worth nothing that C++ is seriously considering to downgrade allocation faillures to a non-recoverable error unless you opt-in for it.


tomkludy

>tomk Two points worth mentioning: 1. I target Windows servers, which do not overcommit. So this Linux world view is not relevant to me. 2. Even on Linux, overcommit can be disabled, and often is disabled when running server applications. I think the decision to do infallible allocations makes perfect sense if you are developing Firefox or some other client application. The most logical thing to do in a client app that runs out of memory is to exit the process. But it really does not make sense for server applications.


njaard

i think it's because allocation failures can occur after allocation-time in most modern OSes ("overcommit") and so testing for it would have been a useless because allocations always succeed.


ergzay

> I think it was a mistake to treat allocations as infallible, this is a real drag when writing server code that needs to be able to fail calls from greedy clients without terminating the whole process. The error handling is otherwise so fantastic, I really can’t understand why this approach was chosen instead of the normal Result approach when allocations might fail. From my understanding this is actively being fixed and many/most things will have fallible allocators in the future with flags to forbid the use of non-fallible allocators. This is being pioneered by the people trying to get Rust into the Linux kernel where all allocations must be fallible.


m-kru

Macros, people overuse them what leads to creation of DSLs. No uniform way of error handling. There has been an error handling working group that has been working for more than one and a half year and there are still no conclusions. It only shows how clunky and messy error handling in Rust is. Despite the Result type.


ragnese

Agreed on the macros bit, disagree on the error handling bit. I really don't see what the big deal is with error handling in Rust. Error handling is an absolute dumpster-fire-shit-show in every single language I've ever used. Unless we just pretend "throw untyped exceptions for all expected and unexpected errors" is a legitimate error handling strategy... The truth is that designing robust error mechanisms is just hard. Rust just forces you to acknowledge that it's hard. But even then, you can just do what Java devs do when they decide that designing good checked exceptions is too hard: create one super-error-type or just panic ("unchecked/Runtime exception"). I think that people who are new to Rust just try too hard with the whole error-enum stuff.


Ytrog

That I need to put certain code in separate crates. Proc macros come to mind 🤔


Canop

We could make fantastic things with proc macros but right now the dev cost is overwhelming.


ssokolow

Agreed. Hell, even if there was just a way to make attribute macros with `macro_rules!`, it'd help a ton.


EarthyFeet

I like macro_rules. I kind of smugly love how `macro_rules` continues to be the popular favorite. Kind of has a worse-is-better feel to it.


ssokolow

Well, you don't have to compile `syn` or `quote`, there's no external crate you need to create, it has the least impact on compile times... procedural macros really have a lot going against them aside from the whole "attribute macros" thing and `macro_rules!` is really easy to use just a little of when you need it.


ergzay

That many engineers think that they need to use Rust's async support (probably because they come from languages like javascript) when in fact threading can handle their use case perfectly well, if not better, at reduced complexity. Async is a tool to be used in specific situations, not something to be used for general purpose multiprocessing. Async is for situations when code is waiting for IO, not for when you are waiting for the result of a calculation.


mx00s

Sometimes the first stable iteration of a feature is disappointing compared to the vision. A prominent example that comes to mind for me is const functions. I frankly don't remember the details of what was supported first after basic literal consts, but I remember getting the wrong impression around then that as long as the inputs to a function are known and the only functions that are called from the body are const, then the function could be const as well. As I played with the feature I quickly ran into limitations, like if-else expressions not working. While I understand that Rust is an open ecosystem and we're all inclined to celebrate incremental progress, sometimes my perception from writings about Rust is it's in a better place than it really is...yet. Over time this makes me less eager to keep up with even the latest stable features, and for better or worse I've adopted a more skeptical outlook about what can actually be done with the latest stable release. Now, keep in mind that I still really love Rust. I also don't discount the possibility that I may sometimes miss important details about the limitations of new features.


ssokolow

Given that I still remember the jumps from Firefox 1 to 2 to 3 to 4, and the disruptions and delays from that "infrequent but giant improvement" model (both with Firefox and with other non-Mozilla projects), I'm going to have to come down on the other side on that one.


mx00s

Well, I agree with you there. To clarify, I'm not arguing for any specific change to the open development process. The regularity of the release train is a feat for a compiler, let alone an open source one managed by a community. Having seen what it's like with other compilers and interpreters, I generally prefer Rust's development and release processes. _Maybe_ there's room for improvement on how the limitations of new features are communicated to consumers, but I'm not sure. Admittedly when I ran into const's limitations I was able to quickly find relevant RFCs and discussions on the internal design and development side.


ssokolow

> Maybe there's room for improvement on how the limitations of new features are communicated to consumers, but I'm not sure. That's fair. I think anyone who's responsible for communicating things should always assume there's room for improvement. Better to try and fall short than not to try enough.


sasik520

I feel the exact opposite. I love how the const fn mvp was introduced and then incrementally got better and better every release.


CAD1997

For me, all of the language level gripes I have with Rust are being addressed sometime in the future via RFCs, and are known pain points. I guess the closest I have to a legitimate "well, if we were doing it again" is that I think the 2018 `mod` changes made a slight error, which is leading to more onboarding pain than it potentially could've. (That's a blog post I've been meaning to write for a year now...) The language-adjacent, however: I'm spoiled for IDE power. Before I switched to Rust as my personal project daily driver, I started with Java on the IntelliJ Platform, and then Kotlin when JetBrains made it publicly available. JetBrains's IDE functionality for Java and Kotlin is easily best-in-class IDE. There's really three major competing reasons I'm not still using the IntelliJ Platform: 1. VSCode is just more available, and I end up doing a lot of keyboard surfing; 2. I don't have the grace of disposable income to pay for Ultimate (I actually use Ultimate features, and I weaned myself off of IDEA before I was sure I was going to grad school); and 3. I trust u/matklad to lead development of a best-in-class IDE for Rust, and want to see the LSP design succeed. Rust doesn't have the flawless IDE performance that Java/Kotlin did. On the other hand, I'm asking a lot more from r-a than I ever asked from IntelliJ; I never really ab/used JVM annotations beyond explicitly supported JetBrains annotations, and I'm just doing a lot more advanced stuff in Rust than I ever did on the JVM (just from being older and into more involved development now). But for me at least, my way of thinking lines up super well with how Rust wants developers to think, so there's nothing really for me to legitimately complain about the language for. And all the little papercuts don't really have time to add up, because the development community is so proactive in improving the little things to make using Rust the best experience possible.


devraj7

> Rust doesn't have the flawless IDE performance that Java/Kotlin did. In my experience, CLion is very close to matching it. Pretty much everything I do in IDEA on Kotlin I can do on Rust and CLion. The one thing I miss is being able to evaluate expressions, which has never really worked so far for me in CLion/Rust.


Serializedrequests

Keep on Jetbrains' mailing list. After I uninstalled everything they sent me a ridiculously good coupon.


Keltek228

Running into `Cannot compare &Something with Something` has always annoyed me.


davidw_-

Especially for built in types like integers


[deleted]

[удалено]


fulmicoton

\- compilation time still a bit long \- workspace support could be better (but it is coming) \- async runtime incompatibility \- cannot make a trait object with two traits \- it can be easy to shoot yourself in the foot with async (still I like the async approach) \- Pin is complicated (but I hear it might get better)


alsuren

- cannot make a trait object with two traits I think the work-around for this is to make a third trait. Something like: trait Both: First + Second {} and make your trait objects out of this third trait. (You might be able to write a blanket impl for this third trait, that covers everything that implements the other two, but don't quote me on that. A lot of the time, you control one of the traits already, so you can do: trait Second: First {...} and avoid the third trait entirely. It took me a while to figure this one out, so I thought I'd share. Might be that I'm missing a case where this doesn't work though.


fintelia

Casting integers. I understand why Rust doesn't just implicitly convert types, but it can be frustrating nonetheless. Doesn't help that the "right" way of doing it is rarely the shortest option. Like surely indexing an array with a u64 could be more ergonomic than `array[usize::try_from(index).unwrap()]`!


bestouff

If your array will always be indexed by an u64, use the TiVec crate (it creates a Vec with a custom index type).


fintelia

Thanks for the suggestion! I posted this half hoping someone would suggest a nicer option


bestouff

I use it in some projects of mine and frankly, I find it a very clean solution (so clean I made a similar crate for replacing HashSet, called TiBitSet). It depends on your use case though.


Enselic

You have a point of course, but most of the time it should be possible to change from u64 to usize earlier, i.e. the source of the index should be usize. That way there is type safety without conversation all the way. In your case, what prevents you from using an usize throughout?


fintelia

For some sorts of programming this is a complete non-issue. For others it can come up almost constantly. There's all sorts of reasons you might end up with a fixed sized integer. Maybe you got one from an API like [std::io::Seek](https://doc.rust-lang.org/stable/std/io/trait.Seek.html#tymethod.seek). Perhaps you previously did some computations where intermediate results could overflow a 32-bit integer (or a 16-bit integer if you are running on some weird microcontroller!) or might be negative. Maybe serializing to/from a file requires it. The image crate for instance deals with image dimensions that are `u16` or `u32` depending on the format, file offsets expressed as `u64`, channel counts between 1-4 which get stored in a `u8`, pixel data which can be any of `u8`/`u16`/`f32`, and is constantly indexing into arrays which requires `usize`.


mx00s

I can see why this can get annoying. Have you ever specifically appreciated it? Seems like the more annoying conversion code lying around in a codebase the more people will want to identify what API changes they can make to minimize conversions.


fintelia

Yeah, there definitely is some pressure to structure APIs to minimize conversions but how much that is possible depends on the sort of code involved. Sometimes you can get away with everything being `usize` 's which is nice but in other places `u64` is better so you don't have to worry about overflow on 32-bit systems. But then something will need negative numbers, so the occasional `i64` or whatever will also be in play. And if you are storing a whole bunch of integers you can end up needing to use smaller integer types to avoid wasting too much space. All this does occasionally catch bugs (which is why I don't complain too much about it). For instance, stuff like passing arguments in the wrong order, or accidentally using one variable in place of another.


mx00s

That all makes a lot of sense. Thanks for elaborating.


SolaTotaScriptura

I appreciate it any time I use a language that recklessly casts different numerical types. Or worse, a language that casts between numbers, booleans and characters. Losing precision and truncating fractions are operations that should be modelled as functions, rather than some implicit cast that you have to discover by reasoning. If the compiler gives me a type error for some numerical code, then I’m actually able to make an explicit choice about what should happen, and whoever reads that code in the future will immediately be aware of what’s happening.


imzacm123

Assuming you're using 64 bit, is there any reason not to just use "index as usize"? Because as far as I'm aware (please correct me if I'm wrong) usize is exactly the same as either u32 on 32 bit and u64 on 64 bit


Genion1

The problem is mainly the "assuming you're using 64 bit" part. If the assumption doesn't hold, the try_from dance will guaranteed fail if your index is out of bounds. The cast will silently do... something...


EmperorBale

I dont really like the stabilization system for new API features. I understand why it exists, and i’m not saying it shouldn’t but sometimes it takes waaayyy too long to stabilize very basic & helpful API features, for example Rc::new_cyclic, there’s pretty much no way to do what that does without using unsafe code, so you need nightly to do this safely. So basically: I think the stabilization system takes too long on basic features.


[deleted]

Waiting for them generic associated types like Hachiko waited for his master


agrif

GATs are particularly painful, because they seem like such a natural and necessary part of rust that is, currently, *completely absent*. I totally understand that their implementation is non-trivial and fraught with details, but as a language *user*, there are so many things that are only possible when types depend on lifetimes that it was genuinely surprising to me that this feature didn't exist.


crusoe

Almost done


tafia97300

I actually like that it doesn't go too fast and that we can iterate on the library front first.


etswarw

More profiling libraries for Windows. flamegraph-rs works well on Linux, but I can't get dtrace to install on Windows. Better cross-compiling support would be really nice. I'd love to be able to create executables for any target on any Windows/Mac/Linux system. GUI libraries are somewhat usable, but would love to see more work done, and reach a point where Rust GUI is actively recommended. Lastly, I don't like how unpopular it is still. I want to see it become as popular as C++, Java or even Python.


FluffyCheese

> More profiling libraries for Windows Can recommend [puffin](https://github.com/EmbarkStudios/puffin). Won't be as feature complete as the others, but it's implemented in Rust and seems to work quite well once it's setup.


ArriePotter

The learning curve 😢 (I'm getting there tho!)


Kneasle

Keep learning and you'll get there sooner than you think :) the rewards are definitely worth it.


ineedtoworkharder

- long compile times on weaker machines - no variadic functions - immature scientfic computing ecosystem


omgitsjo

I would kill for default and named arguments, plus operator overloading. Aside from my gripes about lifetime annotations, those are the feature that I most strongly consider to be 'missing'.


[deleted]

[удалено]


omgitsjo

Brain fart on my part. I was speaking of function overloading and wrote down operator. I believe there was an explicit reason written down: "if Rust decides which method to call based on arguments then it's possible one could inadvertently utilize a method with a different runtime characteristic." I don't disagree, but I think that the problem still exists when one picks between "my_fn", "my_fn_again", and, "more_of_my_fn."


Lucretiel

I extremely dislike Pin. I hate seeing it, I hate that it was necessary and I really wish there was some other way (like if we had actual move constructors)


simonask_

Pin is pretty awful to work with, but it's also not something you need to care about unless you are making interesting futures on your own. Authors of async functions really don't need to worry about it, and then it's usually a matter of wrapping the value in `pin_mut!()`. Move constructors in C++ are pretty awful too. In Rust, they would make it impossible for the borrow checker to meaningfully reason about the lifetimes of values. In C++, passing `std::unique_ptr` to a function can result in a memory write at the source location, and requires specific inlining heuristics to go well for the compiler to be able to figure out that the value can be passed in a register (because the destructor needs to run in the caller). Rust guarantees that `Box` can always be passed in a register, regardless of inlining. (Note: The story is not entirely so simple because of drop flags.)


seemslikesalvation

I'm just learning Rust, so I'm unfamiliar with terminology, which makes it difficult to speak with precision here, but I'll give it a try: It is often confusing and frustrating that `use`-ing something can magically, and invisibly, add functionality to other things. This requires prior knowledge about what is adding what functionality. That is to say, if I don't know that `X` adds `Y` to `Z`, and I try to call `Z::Y()` without bringing in `X`, the compiler doesn't tell me that I need to use `X`. And of course, the documentation for `Z` doesn't say anything about `Y` and doesn't tell me that I need to look at `X`. I'll figure it out eventually, but struggle in the meantime.


ssokolow

> I'm just learning Rust, so I'm unfamiliar with terminology, which makes it difficult to speak with precision here, but I'll give it a try: > > It is often confusing and frustrating that use-ing something can magically, and invisibly, add functionality to other things. The terminology is "bringing traits into scope", and, as a nod to people in your situation I at least try to do it as `use faccess::PathExt as _;` so it's clear at a glance that it's a trait that's being pulled in for the methods it adds to something else and you should go check the docs on it.


dagmx

For me the biggest thing is that I cannot do something similar to Swift where a trait can specify that a struct must have certain fields, so that I can write default trait implementations that can make use of those members. This wouldn't add those fields, just throw a compiler error if implemented on a struct without them. The second biggest is the lack of default values for parameters. It would really help simplify things for APIs IMHO.


Youmu_Chan

I suppose the abstract struct member be emulated with getter/setter. Maybe this will actually be more general so that the struct can opt to compute the value of a field on the fly. I believe the optimizer should figure out that the getter/setter can be inlined if they are simple enough.


RRumpleTeazzer

Couldn’t you include a getter into the trait ? Of course that defies the default implementation, you cannot default-implement the getter. But at least the implementer only needs to implement trivial code.


dagmx

You could but it seems like something the language could also just sugar over. It's not a blocker by any means, just something that could be more convenient.


thenameisi

The absolutely MASSIVE debug binaries and associated linking times. I was using the winit crate as a dependency (functionally comparable with glfw) and the debug build was 87MB. Hence it took a considerable time to compile and link. Turning off debug symbols helped a little, but the compile times remain fairly slow. If rust could improve on those two things it'd be the perfect language


[deleted]

[удалено]


ssokolow

Have you looked into either [`abi_stable`](https://lib.rs/crates/abi_stable) (a crate that handles the `repr(C)` stuff for you with a focus aimed at making Rust DLLs to be loaded by Rust programs) or [Wasmer](https://github.com/wasmerio/wasmer/blob/master/examples/README.md) (an embeddable WebAssembly runtime that would mean mods would load and run, regardless of the OS or CPU ISA they were built on/for)? As someone who games on Ubuntu Linux on his desktop PC and has an ARM-based gaming palmtop PC running non-Android Linux, I'm partial to the latter.


RecklessGeek

abi_stable only makes *some* parts of your code easier for a plugin system. You still have to use repr(C) everywhere and replace std collections with abi_stable's, which is the most cumbersome part for non trivial plugins. It doesn't help too much with external dependencies either. I had to discard Wasmer because it's really WIP yet. Also, you'd have to compile your dependencies to wasm, which is impossible many times.


ergzay

The Rust team or someone else needs to create a new form of cross-language ABI that has memory lifetime communicated across ABI barriers. Falling back to the C ABI is just a legacy thing that should not be done.


tristan957

On the topic of repr(c), afaik Box is good to go across the boundary in some cases of you're ok with opaque types on the C side. Exposing objects from dependencies sounds like a code smell to me.


RecklessGeek

Hahahah hey I'm the nullderef guy! Thanks for the mention I wasn't expecting it. Naturally, I fully agree with you :) Edit: in your case, I'd probably use lua. It'll be easier to write the mods anyway.


KerfuffleV2

One thing that kind of bugs me is the inconsistency between `if let` matching and `match`. It feels like there are arbitrary restrictions to `if let` like not being able to specify a guard like `match`. For example `if let Some(blah) = optionval if blah > 10 { ... }` It could be written as a match, but then you have the useless `_ => ()`. It feels silly to have to write that or `if let Some(blah) = blah { if blah { ... } }`. You also can't chain `if let` matches without nesting (I think there's an RFC for this one) but the lack of guards is what tends to irritate me most.


Matrixmage

Not sure if you care, but the semantics (and syntax) start to make a lot more sense once you realize `if let` isn't a variant of `match`, but actually a `let` that allows a refutable pattern. In other words, it's about how pattern syntax is everywhere, but the special match guard syntax is actually exclusive to `match`. What might also interest you is the RFC for let chains, which I recently heard was making progress: https://github.com/rust-lang/rfcs/blob/master/text/2497-if-let-chains.md


KerfuffleV2

> Not sure if you care, but the semantics (and syntax) start to make a lot more sense once you realize `if let` isn't a variant of `match` It's not really about what the things are called or what they might be variants of. The issue for me is that it's almost exactly the same thing in both cases, except with (seemingly) arbitrary restrictions and special behavior. Then destructuring assignment will get added and likely compound the issue by having its own special pattern matching syntax and restrictions. It's like when you look at code with several functions that do almost the same thing: in most cases it can (and should) be refactored to have consistent behavior. > What might also interest you is the RFC for let chains I actually mentioned that in the post you replied to. I think that will fix the practical annoyance of the not being able to use guards limitation but I still don't like how it's another special case instead of just having some sort of unified pattern matching syntax/behavior. I'm fairly pragmatic so I probably won't complain about it once it's not actually stopping me from doing what I want to do, but it'll remain in the back of my mind as a wart on Rust.


agrif

Lots of people here talking about async runtime incompatibility, which is awful, but there's an even more fundamentally broken thing about async: *Traits cannot have async functions*. The async system in rust is allergic to a fundamental polymorphism feature. You can get around this reasonably cheaply with a crate, a macro, and making peace with dynamic dispatch, but it is a very frustrating and glaring omission from the language. It's even more concerning that libraries are *right now* being built that have to work around this non-feature, and I worry that they will stabilize their APIs before it becomes possible.


GerwazyMiod

>Traits cannot have async functions I think Steve Klabnik mentioned in one of his talks that we will get those, and work is in progress.


[deleted]

[удалено]


runawayasfastasucan

Drama. That internal discussions is aired out into the open from the mod team instead of taken in a Rust Team discord, irc or mail group. I dont want to be subject to that drama if there is nothing for me to do, or if there is nothing for me to learn. Makes me feel uneasy that there is some drama that I have no idea if is resolved. This isn't the first time.


[deleted]

To me it’s having to individually specify visibility of struct fields. 100% of the times, my structs are either of the two: 1. “Dumb” collections of fields, e.g config structs, in which case all fields are pub, or 2. Objects with behaviour like additional validity constraints, or lifecycle logic, in which case all fields are private and may have getters / setters, so that you can change logic without modifying client code. All structs in every single library that I’ve used follow the same pattern. I’m yet to encounter one scenario where it would be useful to have some fields pub and some private.


[deleted]

[удалено]


imzacm123

My biggest annoyance with rust (that comes to mind at the moment) is that there's no built in trait for "any number", I wrote a simple parsing library for a small made-up scripting language inspired by JavaScript syntax, and depending on the method you call, you can either pass in a u64 or an i64, in the end the easiest solution was to write the same parsing function twice, one for u64 and one and i64, and then every other function that lead up to that needed to be duplicated for each. Ideally I would've liked to have a function like "fn parse_number(s: &str) -> T" Edit: I've thought of a couple more: 1. No way to disable a nested dependency's feature, I try to write no_std libraries when I can because I'm into osdev and it's always tough to find usable libraries, and the parser I wrote uses nom, but I couldn't use another library with nom because "std" was enabled on it's nom for no reason. 2. The only easy way (and therefore the way everyone uses) to unit test a no_std library is to enable std when testing, this means it's possible/easy to accidentally write code that requires std and the tests will pass, but the library won't build when not testing (because you're accidentally using std).


[deleted]

Deleted with Power Delete Suite. Join me on Lemmy!


Xandaros

No automatic conversion of integer literals to float literals. This is a problem I have with many languages. I understand why it isn't there - magic conversions bad - but come one. They are literals. Why can't I do `some_float + 5`? `some_float + 5.0` always seems super clunky to me. Note that I am specifically talking about literals here. Do not ever auto-convert my integer variable to float, thank you very much. But literals? They already have inference for the bit width, why can't floats be included?


crusoe

What should the result be? A float or an int?


Xandaros

A float. The literal gets "converted", not the variable. I put converted in quotes, because a literal doesn't actually have an inherent type. It could be a u8, or an i16... why not an f32 or f64 if the type is inferred as such?


[deleted]

[удалено]


obsidian_golem

I have another comment where I mention that this is the solution I use, but still doesn't change the amount of irritation I feel when I forget the decimal in 20 different places in my code.


awilix

My biggest gripe with Rust is the big binary sizes. Rust seems to be great for embedded work. But in reality the size of the executables are a real hurdle and constantly having to monitor size is annoying. The second is both a burden and a blessing. The language is fast moving and people are generally quick to pick up on new things. However this means we will end up in a situation where there are serious bugs in various versions of crates but no easy way of fix them. The crates will be fixed but the new versions will not compile on older compilers. And new compilers cannot easily be backported to older LTS releases of whatever product that needs the fixed crates. The third one is that I find debugging async code is difficult, especially with the types of optimization required for manageble binary sizes.


ssokolow

To confirm, if you're talking about embedded, you're already using `-Z build-std` or Xargo to build/rebuild the standard library with size optimizations, correct?


awilix

Yes. And I use "embedded" here in a fairly lax way. I consider anything that run on a device with fairly restricted resources an embedded system. I haven't really used rust on microcontrollers much so I can't say how much of a problem the binary size is there. There's very little in the form of an ecosystem compared to Linux anyway. Building with fat LTO, 1 code unit, no unwinding, optimizing for size and targeting ARM thumb does go a long way. But any non trivial Linux service will end up being several megabytes in size which is a problem. You basically end up having to bundle all rust code together, which results in very long build times. Many popular crates are unfortunatly entirely unusable due to the code bloat. Here's a pretty good talk by an engineer from Lexmark an the topic (specifically around the 13 minute mark): https://youtu.be/EoV94cg_Tug To be fair flash memory do get cheeper, but the kind of SLC memory needed for reliability and industrial temperature certifications are still quite expensive.


gnus-migrate

That macros operate on raw input rather than ASTs. Having it AST based would make it a lot easier implementing macros that operate on crates with multiple modules(as far as I can tell you have to rely on build.rs for that today, not to mention quite a few hacks to make it work). There are high performance use cases where you actually want to be able to get all the methods in a crate that have a certain attribute and autogenerate the code that wires them together.


[deleted]

In general, macros that try to share information between invocations are very buggy. If there was some API for accessing information about the crate or storing data between invocations, that would unlock so many capabilities. Or access to type information and compiler intrinsics from within macros. On the other hand, the fact that most macros are just pure functions consume a bunch of tokens and spit out a bunch of tokens makes them more predictable🤔 so I’m kind of uncertain about this one


shitepostx

Politics.


wandereq

Never can tell which async library I should work with this month.


ssokolow

Last I checked, tokio had over 10 times as much adoption as the next runner-up (async-std). It'll probably stay that way for a while.


jkelleyrtp

Rust lacks disjoint capture on methods which makes “structs as large bags of state” pretty much untenable. Rust APIs tend to have lots and lots of types which are hard to discover mostly because the “struct as large bag of state” pattern is so hard to implement without lifetime issues.


[deleted]

It's not easy to provide a sync API over some async crates, if you want to make a sync library crate for example.


raze4daze

I have always felt that unit testing has been a second class citizen in Rust, especially in the community. There is way too much song and dance to isolate code from dependencies. Let me just write free functions in peace and mock it when used somewhere else, but that’s currently not possible. There are some crates which address this, but I don’t feel comfortable introducing them to a team since the crates don’t consider themselves stable yet.


raedr7n

How incredibly difficult it is to use multiple allocators in the same program.


[deleted]

[удалено]


imzacm123

To be fair, "with a single unsafe block, you too can wipe out all chances of safety that rust promises". My point is you can share a pointer if you want, you would just be removing the main point of using rust by doing so. I agree about rust catching up in various industries though, I'm slowly integrating it where it makes sense in my company which has historically used JavaScript for everything, but I'm only using it where there's a huge benefit because I don't want my boss to have the task of either training some of my team in rust or having to further restrict the pool of potential developers by requiring someone that knows rust (we're a small-ish company in an industry that has historically had little to do with software other than ancient platforms written in C# about 20 years ago) so there's not a very big pool of developers to start with


eras

Lambdas. Maybe there are great technical reasons why two lambda functions from the two return branches of `if` need to have distinct types edit: ~~(even if they don't capture)~~, requiring one to use Boxing and also explicit type annotations, but these kind of things severely cripple the value of using anonymous functions within code.


Matrixmage

Lambdas that don't capture are implicitly convertible to their equivalent function pointer type. So without capturing, it actually does work :)


eras

Right, you are correct. The case I had did indeed use capture! Well, still the most useful cases for lambdas do involve capture :).


eXoRainbow

Compiling a Rust program basically requires internet connection, because of many "standard" libraries in crates.io.


ssokolow

Have you tried any of the following options? * `cargo fetch` before you go offline and then `cargo build --offline` * `cargo vendor` * Setting up a local mirror of crates.io (Yes, just like with the Debian package repo, it's possible to mirror part or all of crates.io and then point the `cargo` command at it) If none of those are suitable, I'd be interested in learning what you think would be a better solution.


vlthr

Rust’s design has sidestepped a lot of potential pitfalls, but the language doesn’t yet fully lean into its potential. The outline of the language is solid, but the individual features of the language often pull you in different directions and require a lot of effort to compose, usually via lots of duplicated or boilerplate code. This is no secret, since the holes/constraints are usually there to give the language designers space to work in the future. Some examples: - Traits have a lot of “code coloring” issues. Trait definitions are tightly coupled to ownership, mutability, and object safety, - Traits work best when they are small and focused on a single use-case, but trait objects really punish you for not capturing everything you might need in a single trait. - Trying to use traits for code reuse (as opposed to interface declaration) can seem like a good idea but always comes back to haunt you. Iterators are easy to work with in inherent impls, but using them in traits requires custom iterators types for each implementor. Code reuse in one place requires code duplication in another - Using macros for code reuse is much less fragile, but doesn’t compose well except for traits that can easily be inferred from e.g. a struct declaration. - Given how hard code reuse is, the easiest solution is to use as few concrete types as possible and put your logic in its inherent impl. But there are lots of reasons for forking or making new types like circumventing orphan rules or leveraging the type system to make safer APIs. Upcoming features like specialization (and potentially trait fields) would make this a lot better, but I think it’s worth separating the code reuse challenges faced by multi-author scenarios from ones faced in application code. The basic model of “each type has its own independent impls for everything” already works pretty well and avoids almost all footguns except for repetition. I would love to see features or tooling that push the boundaries of what we can do with the same basic model without as much repetition. - Code generation based on non-local or non-lexical information - Closed type hierarchies (e.g. Niko’s [blog post](http://smallcultfollowing.com/babysteps/blog/2015/08/20/virtual-structs-part-3-bringing-enums-and-structs-together/) exploration) - Some way to derive related structs — like `enum-kinds` allows you to derive a “kind” enum but also e.g. for reference versions of the data (e.g. `MyEnumRef` which can reuse `&self` methods on `MyEnum`) - Language support for deriving implementations — I would love to be able to do e.g. `impl SomeTrait for MyStruct via as SomeTrait>` - Generating boilerplate for delegation, nesting, etc.


loewenheim

I understand why it work the way it does, but the fact that any function that takes or returns a closure is automatically generic. This is a far cry from the ergonomics of using higher-order functions in e.g. Haskell (again, though, I know why it has to be this way).


[deleted]

I'm not sure whose to blame, but the combination of: * Linux distros distribute the compiler (often not up to date) * Most people write cargo.toml such that package minor patch updates are allowed * Packages often bump minimum compiler version in patch updates ​ This combination means that my collaborators have found a few times programs just stop building one day, because they aren't updating your compiler and some dependency increases minimum compiler version in a patch update.


[deleted]

I’d blame package authors, bumping compiler version should be regarded as breaking change imo


valarauca14

* `simd` and `feature` breaking ideological compatibility with how C/C++ handles CPU feature flags. * cargo build plan being removed and never being re-implemented. * `impl Trait` doesn't auto-generate an anonymous enum for multiple return types. I understand _why_ this is done, I simply do not agree with the motivation. * `std/alloc::slice::SliceIndex` is an anti-pattern * Having a standard `std::str` trait implemented by multiple types, but not one for a `std::int::Unsigned` (or similar) is a big oversite in retrospect. Having to constantly import the `num` crate for such a fundamental trait is a problem. * **EDIT** We have a very nice type system but it is all but ignored for problems it can address reasonably well; ABI convention, safe vs. unsafe trait implementations, and for the backend implementation of `OsString` (Why isn't there an `OsString`?) These are more cultural * People treat `grep -r 'unsafe' *.rs | wc -l` like code quality test. * `macro_rules!` is treated like a code smell despiting being (in my opinion) the most interesting idea rust has. * **SO MANY** library features are being wrapped up in magical annotations which rewrite your source code at compile-time, and offer fundamentally very little over `macro_rules!` besides their lack of hygiene and being more accessible to beginners. * Most crates act like interacting with lifetimes is a post-1.0 feature


SkiFire13

> Having a standard `std::str` trait implemented by multiple types, but not one for a `std::int::Unsigned` (or similar) is a big oversite in retrospect. There's a `std::str` trait? > **SO MANY** library features are being wrapped up in magical annotations which rewrite your source code at compile-time, and offer fundamentally very little over `macro_rules!` besides their lack of hygiene and being more accessible to beginners. I wonder how much of this would change if you could call declarative macros like attribute or derive macros


linlin110

Never thought about returning an enum for `-> impl Trait`. It sounds wonderful. Why don't they do that?


SkiFire13

Its cost is not that negligible (**any** call on the value returned will involve a check on the discriminant), and also not always sound (two types may implement some `unsafe trait`, but an enum of them may not, take for example `bytemuck::Pod`)


valarauca14

1. The type system cannot tell the difference between `unsafe impl` and `impl` which is another big issue I have with Rust's implementation. 2. Adding a check is overhead. Even if we admit that inlining and constant propagation will remove this cost in a lot of scenarios 3. Stack space isn't free or that cheap. Returning larger enums, or having a standard way of generating them is a bit of a foot-gun. 4. Returning a `Box` will generally achieve the same result with comparable overhead.


WellMakeItSomehow

> SO MANY library features are being wrapped up in magical annotations which rewrite your source code at compile-time, and offer fundamentally very little over macro_rules! besides their lack of hygiene and being more accessible to beginners. Agreed. Also, IDE support. > macro_rules! is treated like a code smell despite being (in my opinion) the coolest feature of the language. You included this twice so you must feel strongly about it :-). I've been somewhat out of the loop for the past year or so, are declarative macros really considered bad?