[HN Gopher] Pitfalls of Safe Rust
       ___________________________________________________________________
        
       Pitfalls of Safe Rust
        
       Author : pjmlp
       Score  : 108 points
       Date   : 2025-04-04 17:55 UTC (2 days ago)
        
 (HTM) web link (corrode.dev)
 (TXT) w3m dump (corrode.dev)
        
       | woah wrote:
       | Is "as" an uneccesary footgun?
        
         | whytevuhuni wrote:
         | That was my first impression as well. So much of Rust's
         | language and standard library enforces correctness, that gaps
         | start to feel way more visible.
         | 
         | "as" is a good example. Floats are pretty much the only reason
         | PartialEq exists, so why can't we have a guaranteed-not-NaN-
         | nor-inf type in std and use that everywhere? Why not make
         | wrapping integers a panic even in release mode? Why not have
         | proper dependent types (e.g. to remove bound checks), and
         | proper linear types (to enforce that object destructors always
         | run)?
         | 
         | It's easy to forget that Rust is not an ideal language, but
         | rather a _very pragmatic_ one, and sometimes correctness loses
         | in favour of some other goals.
        
           | bombela wrote:
           | I have been following rust very closely since 2013.
           | 
           | As Rust is both evolving and spreading wide, we; the
           | programmers, users of Rust; are also leveling up in how we
           | approach correctness and design with it.
           | 
           | Maybe the next evolution will be something like Haskell but
           | fast like Rust is fast like C without the pain of C++.
           | 
           | But it takes a while for the world to catch up, and for
           | everybody to explore and find ways to work with or around the
           | abstractions that helps with correctness.
           | 
           | It's a bit like the evolution from a pointer to some malloc
           | memory, then the shared/unique pointer of C++, to the fully
           | safe box/(a)rc of Rust.
           | 
           | It might be obvious today how much more efficient it is
           | programming with those abstractions.
           | 
           | I see some similarities with functional programming that
           | still seems so niche. Even though the enlighteneds swears by
           | it. And now we actually seem to be slowly merging the best
           | parts of functional and imperative together somehow.
           | 
           | So maybe we are actually evolving programming as a species.
           | And Rust happens to be one of the best scaffold at this point
           | in history.
           | 
           | Thank you for reading my essay.
        
             | pjmlp wrote:
             | There is hardly any evolution from pointer to malloc, C is
             | one of the few systems languages, including those that
             | predated it, where one needs math to allocate heap memory.
             | 
             | I do agree that the evolution is most likely a language
             | that combines automatic resource management with
             | affine/linear/effects/dependent/proofs.
             | 
             | Or AIs improve to the point to render all existing
             | programming languages a thing from the past, replaced by
             | regular natural languages and regular math.
        
           | hansvm wrote:
           | > guaranteed-not-NaN-nor-inf
           | 
           | Nor negative zero
        
           | adgjlsfhk1 wrote:
           | The other option would be to change how floating point works.
           | IEEE specifies operations, not names, so it would be totally
           | valid to have <= on floats be a total order (using integer
           | cpu instructions), and make a function called
           | IEEEAreIdiotsWhoThinkThisIsFloatingPointLessThan which is the
           | partial order that sucks.
        
             | wongarsu wrote:
             | For purposes of sorting, Rust does offer a non-IEEE order
             | as f64::total_cmp. You can easily build a wrapper type that
             | uses that for all comparisons, or use a crate that does it
             | for you
             | 
             | https://doc.rust-
             | lang.org/std/primitive.f64.html#method.tota...
        
               | zozbot234 wrote:
               | total_cmp is precisely IEEE's separately specified total
               | order for floats. It's just that the more common
               | operators do something different, and that's perhaps
               | better for most uses where NaN are inherently unexpected
               | and generally indicate that some kind of error condition
               | has occurred.
        
           | thayne wrote:
           | There is some movement towards deprecating "as", and lints
           | that will recommend using alternatives when possible, but
           | there are a couple of cases, such as intentional truncation,
           | where there isn't a stable alternative yet.
        
           | int_19h wrote:
           | Some of these don't strike me as particularly pragmatic. E.g.
           | are overflow checks really _that_ expensive, given that it 's
           | a well-known footgun that is often exploitable? Sure, you
           | don't want, say, 10% overhead in your number-crunching codec
           | or whatever, but surely it's better to have _those_ cases opt
           | in for better perf as needed, as opposed to a default
           | behavior that silently produces invalid results?
        
         | eptcyka wrote:
         | When fitting larger types into smaller ones? Yes.
        
         | bombela wrote:
         | Compared to C/C++ "as" feels so much safe r. Now that Rust and
         | we the programmers have evolved with it, I too feel that "as"
         | for narrowing conversion is a small foot gun.
        
           | bigstrat2003 wrote:
           | I'm struggling to see how you would implement narrowing
           | conversion in a way that is harder for programmers to misuse
           | when they aren't being mindful, while also being pleasant to
           | use when you really _do_ want to just drop higher bits. Like,
           | you could conceivably have something like a  "try_narrow"
           | trait which wraps the truncated value inside an Err when it
           | doesn't fit, and it would probably be harder to accidentally
           | misuse, but that's also really cumbersome to use when you are
           | trying to truncate things.
        
             | woah wrote:
             | let foo: u8 = bar<u64>.truncate_to()?
        
         | bigstrat2003 wrote:
         | I wouldn't say so. I quite like "as". It can have sharp edges
         | but I think the language would be significantly worse off
         | without it.
        
           | zozbot234 wrote:
           | The question is not whether the language should include such
           | a facility, but whether 'as' should be the syntax for it.
           | 'as' is better than the auto conversions of C but it's still
           | extremely obscure. It would be better to have some kind of
           | explicit operator marking this kind of possibly unintended
           | modulo conversion. Rust will gain safe transmute operations
           | in the near future so that will perhaps be a chance to revise
           | this whole area as well.
        
           | wongarsu wrote:
           | It's useful to have something that does the job of "as", but
           | I dislike how the most dangerous tool for type conversions
           | has the nicest syntax.
           | 
           | Most of the time I want the behavior of
           | ".try_into().unwrap()" (with the compiler optimizing the
           | checks away if it's always safe) or would even prefer a
           | version that only works if the conversion is safe and
           | lossless (something I can reason about right now, but want to
           | ensure even after refactorings). The latter is really hard to
           | achieve, and ".try_into.unwrap()" is 20 characters where "as"
           | is 2. Not a big deal to type with autocomplete, but a lot of
           | visual clutter.
        
       | nerdile wrote:
       | Title is slightly misleading but the content is good. It's the
       | "Safe Rust" in the title that's weird to me. These apply to Rust
       | altogether, you don't avoid them by writing unsafe Rust code.
       | They also aren't unique to Rust.
       | 
       | A less baity title might be "Rust pitfalls: Runtime correctness
       | beyond memory safety."
        
         | burakemir wrote:
         | It is consistent with the way the Rust community uses "safe":
         | as "passes static checks and thus protects from many runtime
         | errors."
         | 
         | This regularly drives C++ programmers mad: the statement "C++
         | is all unsafe" is taken as some kind of hyperbole, attack or
         | dogma, while the intent may well be to factually point out the
         | lack of statically checked guarantees.
         | 
         | It is subtle but not inconsistent that strong static checks
         | ("safe Rust") may still leave the possibility of runtime
         | errors. So there is a legitimate, useful broader notion of
         | "safety" where Rust's static checking is not enough. That's a
         | bit hard to express in a title - "correctness" is not bad, but
         | maybe a bit too strong.
        
           | whytevuhuni wrote:
           | No, the Rust community almost universally understands "safe"
           | as referring to memory safety, as per Rust's documentation,
           | and especially the unsafe book, aka Rustonomicon [1]. In that
           | regard, Safe Rust is safe, Unsafe Rust is unsafe, and C++ is
           | also unsafe. I don't think anyone is saying "C++ is all
           | unsafe."
           | 
           | You might be talking about "correct", and that's true, Rust
           | generally favors correctness more than most other languages
           | (e.g. Rust being obstinate about turning a byte array into a
           | file path, because not all file paths are made of byte
           | arrays, or e.g. the myriad string types to denote their
           | semantics).
           | 
           | [1] https://doc.rust-lang.org/nomicon/meet-safe-and-
           | unsafe.html
        
             | pjmlp wrote:
             | Mostly, there is a sub culture that promotes to taint
             | everything as unsafe that could be used incorrectly,
             | instead of memory safety related operations.
        
               | dymk wrote:
               | That subculture is called "people who haven't read the
               | docs", and I don't see why anyone would give a whole lot
               | of weight to their opinion on what technical terms mean
        
               | arccy wrote:
               | I don't see why people would drop the "memory" part of
               | "memory safe" and just promote the false advertising of
               | "safe rust"
        
               | an_ko wrote:
               | It sounds like you should read the docs. It's just a
               | subject-specific abbreviation, not an advertising trick.
        
               | arccy wrote:
               | but it is false advertising when it's used all over the
               | internet with: rust is safe! telling the whole world to
               | rtfm for your co-opting of the generic word "safe" is
               | like advertisers telling you to read the fine print: a
               | sleazy tactic.
        
               | pkhuong wrote:
               | Someone tell that to the standard library. No memory
               | safety involved in non-zero numbers https://doc.rust-
               | lang.org/std/num/struct.NonZero.html#tymeth...
        
               | whytevuhuni wrote:
               | There is, since the zero is used as a niche value
               | optimisation for enums, so that Option<NonZero<u32>>
               | occupies the same amount of memory as u32.
               | 
               | But this can be used with other enums too, and in those
               | cases, having a zero NonZero would essentially transmute
               | the enum into an unexpected variant, which may cause an
               | invariant to break, thus potentially causing memory
               | unsafety in whatever required that invariant.
        
               | zozbot234 wrote:
               | > which may cause an invariant to break, thus potentially
               | causing memory unsafety in whatever required that
               | invariant
               | 
               | By that standard anything and everything might be tainted
               | as "unsafe", which is precisely GP's point. Whether the
               | unsafety should be blamed on the outside code that's
               | allowed to create a 0-valued NonZero<...> or on the code
               | that requires this purported invariant in the first place
               | is ultimately a matter of judgment, that people may
               | freely disagree about.
        
               | Aurornis wrote:
               | I see this subculture far more in online forums than with
               | fellow Rust developers.
               | 
               | Most often, the comments come from people who don't even
               | write much Rust. They either know just enough to be
               | dangerous or they write other languages and feel like
               | it's a "gotcha" they can use against Rust.
        
             | brundolf wrote:
             | Formally the team/docs are very clear, but I think many
             | users of Rust miss that nuance and lump memory safety
             | together with all the other features that create the "if it
             | compiles it probably works" experience
             | 
             | So I agree with the above comment that the title could be
             | better, but I also understand why the author gave it this
             | title
        
             | ampere22 wrote:
             | If a C++ developer decides to use purely containers and
             | smart pointers when starting a new project, how are they
             | going to develop unsafe code?
             | 
             | Containers like std::vector and smart pointers like
             | std::unique_ptr seem to offer all of the same statically
             | checked guarantees that Rust does.
             | 
             | I just do not see how Rust is a superior language compared
             | to modern C++
        
               | criddell wrote:
               | C++ devs need to understand the difference between:
               | Vec1[0];        Vec1.at(0);
               | 
               | Even the _at_ method isn't statically checked. If you
               | want static checking, you probably need to use
               | std::array.
        
               | ddulaney wrote:
               | Unfortunately, operator[] on std::vector is inherently
               | unsafe. You can potentially try to ban it (using at()
               | instead), but that has its own problems.
               | 
               | There's a great talk by Louis Brandy called "Curiously
               | Recurring C++ Bugs at Facebook" [0] that covers this
               | really well, along with std::map's operator[] and some
               | more tricky bugs. An interesting question to ask if you
               | try to watch that talk is: How does Rust design around
               | those bugs, and what trade offs does it make?
               | 
               | [0]: https://m.youtube.com/watch?v=lkgszkPnV8g
        
               | ampere22 wrote:
               | Thank you for sharing. Seems I still have more to learn!
               | 
               | It seems the bug you are flagging here is a null
               | reference bug - I know Rust has Optional as a workaround
               | for "null"
               | 
               | Are there any pitfalls in Rust when Optional does not
               | return anything? Or does Optional close this bug
               | altogether? I saw Optional pop up in Java to quiet down
               | complaints on null pointer bugs but remained skeptical
               | whether or not it was better to design around the fact
               | that there could be the absence of "something" coming
               | into existence when it should have been initialized
        
               | int_19h wrote:
               | It's not so much Optional that deals with the bug. It's
               | the fact that you can't just use a value that could
               | possibly be null in a way that would break at runtime if
               | it is null - the type system won't allow you, forcing an
               | explicit check. Different languages do this in different
               | ways - e.g. in C# and TypeScript you still have null, but
               | references are designated as nullable or non-nullable -
               | and an explicit comparison to null changes the type of
               | the corresponding variable to indicate that it's not
               | null.
        
               | phoenk wrote:
               | The commonly given response to this question is two-fold,
               | and both parts have a similar root cause: smart pointers
               | and "safety" being bolted-on features developed decades
               | after the fact. The first part is the standard library
               | itself. You can put your data in a vec for instance, but
               | if you want to iterate, the standard library gives you
               | back a regular pointer that can be dereferenced
               | unchecked, and is intended to be invalidated while still
               | held in the event of a mutation. The second part is third
               | party libraries. You may be diligent about managing
               | memory with smart pointers, but odds are any library you
               | might use probably wants a dumb pointer, and whether or
               | not it assumes responsibility for freeing that pointer
               | later is at best documented in natural language.
               | 
               | This results in an ecosystem where safety is opt-in,
               | which means in practice most implementations are largely
               | unsafe. Even if an individual developer wants to
               | proactive about safety, the ecosystem isn't there to
               | support them to the same extent as in rust. By contrast,
               | safety is the defining feature of the rust ecosystem. You
               | can write code and the language and ecosystem support you
               | in doing so rather than being a barrier you have to fight
               | back against.
        
               | josephg wrote:
               | Yep. Safe rust also protects you from UB resulting from
               | incorrect multi-threaded code.
               | 
               | In C++ (and C#, Java, Go and many other "memory safe
               | languages"), it's very easy to mess up multithreaded
               | code. Bugs from multithreading are often insanely
               | difficult to reproduce and debug. Rust's safety
               | guardrails make many of these bugs impossible.
               | 
               | This is also great for performance. C++ libraries have to
               | decide whether it's better to be thread safe (at a cost
               | of performance) or to be thread-unsafe but faster. Lots
               | of libraries are thread safe "just in case". And you pay
               | for this even when your program / variable is single
               | threaded. In rust, because the compiler prevents these
               | bugs, libraries are free to be non-threadsafe for better
               | performance if they want - without worrying about
               | downstream bugs.
        
               | int_19h wrote:
               | The standard library doesn't give you a regular pointer,
               | though (unless you specifically ask for that). It gives
               | you an iterator, which is pointer-like, but exists
               | precisely so that other behaviors can be layered. There's
               | no reason why such an iterator can't do bounds checking
               | etc, and, indeed, in most C++ implementations around,
               | iterators do make such checks in debug builds.
               | 
               | The problem, rather, is that there's no implementation of
               | checked iterators that's fast enough for release build.
               | That's largely a culture issue in C++ land; it could
               | totally be done.
        
           | quotemstr wrote:
           | Safe Rust code doesn't have accidental remote code execution.
           | C++ often does. C++ people need to stop pretending that
           | "safety" is some nebulous and ill-defined thing. Everyone,
           | even C++ people, shows perfectly damn well what it means. C++
           | people are just miffed that Rust built it while they slept.
        
             | surajrmal wrote:
             | Accidental remote code execution isn't limited to just
             | memory safety bugs. I'm a huge rust fan but it's not good
             | to oversell things. It's okay to be humble.
        
               | dymk wrote:
               | RCEs are almost exclusively due to buffer overruns, sure
               | there are examples where that's not the case but it's not
               | really an exaggeration or hyperbole when you're comparing
               | it to C/C++
        
               | thayne wrote:
               | Almost exclusively isn't the same as exclusively.
               | 
               | Notably the log4shell[1] vulnerability wasn't due to
               | buffer overruns, and happened in a memory safe language.
               | 
               | [1]: https://en.m.wikipedia.org/wiki/Log4Shell
        
               | josephg wrote:
               | The recent postgresql sql injection bug was similar. It
               | happened because nobody was checking if a UTF8 string was
               | valid. Postgres's protections against sql injection
               | assumed that whatever software passed it a query string
               | had already checked that the string was valid UTF8 - but
               | in some languages, this check was never being performed.
               | 
               | This sort of bug is still possible in rust. (Although
               | this particular bug is probably impossible - since safe
               | rust checks UTF8 string validity at the point of
               | creation).
               | 
               | This is one article about it - there was a better write
               | up somewhere but I can't find it now: https://www.rapid7.
               | com/blog/post/2025/02/13/cve-2025-1094-po...
               | 
               | Rust's static memory protection does still protect you
               | against most RCE bugs. Most is not all. But that's still
               | a massive reduction in security vulnerabilities compared
               | to C or C++.
        
               | FreakLegion wrote:
               | In fact "exclusively" doesn't belong in the statement at
               | all. A very small number of successful RCE attacks use
               | exploits at all, and of those, most target (often simple
               | command) injection vulnerabilities like Log4Shell.
               | 
               | If you think back to the big breaches over the last five
               | years, though -- SolarWinds, Colonial Pipeline, Uber,
               | Okta (and through them Cloudflare), Change Healthcare,
               | etc. -- all of these were basic account takeovers.
               | 
               | To the extent that anyone has to choose between investing
               | in "safe" code and investing in IT hygiene, the correct
               | answer today is IT hygiene.
        
               | surajrmal wrote:
               | Can you back up your 'very small number " with some data?
               | I don't think it lines up with my own experience here.
               | It's really not an either or matter. Good security
               | requires a multifaceted approach. Memory safety is
               | definitely a worthwhile investment.
        
             | yjftsjthsd-h wrote:
             | Research I've seen seems to say that 70-80% of
             | vulnerabilities come from memory safety problems[0].
             | Eliminating those is of course a huge improvement, but is
             | rust doing something to kill the other 20-30%? Or is there
             | something about RCE that makes it the exclusive domain of
             | memory safety problems?
             | 
             | [0] For some reason I'm having trouble finding primary
             | sources, but it's at least referenced in ex.
             | https://security.googleblog.com/2024/09/eliminating-
             | memory-s...
        
               | Xylakant wrote:
               | Rust also provides guarantees that goe beyond mere memory
               | safety. You get data-race safety as well, which avoids
               | certain kinds of concurrency issues. You also get type-
               | safety which is a step up when it comes to parsing
               | untrusted input, at least compared to C for example. If
               | untrusted inout can be parsed into your expected type
               | system, it's more likely to not cause harm by confusing
               | the program about what's in the variables. Rust doesn't
               | straight up eliminate all source of error, but it makes
               | major strides forward in areas that go beying mere memory
               | safety.
        
           | NoTeslaThrow wrote:
           | If english had static checks this kind of runtime pedantry
           | would be unnecessary. Sometimes it's nice to devote part of
           | your brain to productivity rather than checking coherence.
        
           | bigstrat2003 wrote:
           | The problem with the title is that the phrase "pitfalls of
           | safe rust" implies that these pitfalls are unique to, or made
           | worse by, safe rust. But they aren't. They are challenges in
           | any programming language, which are no worse in rust than
           | elsewhere.
           | 
           | It's like if I wrote an article "pitfalls of Kevlar vests"
           | which talked about how they don't protect you from being shot
           | in the head. It's technically correct, but misleading.
        
       | IshKebab wrote:
       | For integer overflows and array out of bounds I'm quite
       | optimistic about Flux
       | 
       | https://github.com/flux-rs/flux
       | 
       | I haven't actually used it but I do have experience of refinement
       | types / liquid types (don't ask me about the nomenclature) and
       | IMO they occupy a very nice space _just before_ you get to
       | "proper" formal verification and having to deal with loop
       | invariants and all of that complexity.
        
       | quotemstr wrote:
       | Some of this advice is wrongheaded. Consider array indexing:
       | usually, an out of bounds access indicates a logic error and
       | should fail fast to abort the problem so it doesn't go further
       | off the rails. Encouraging people to use try-things everywhere
       | just encourage them to paper over logic bugs and leads to less
       | reliable software in the end. Every generation has to learn this
       | lesson anew through pain.
        
         | bombela wrote:
         | I think what you are saying is that there must be an informed
         | decision betwen crashing the program vs returning an error.
         | Instead of returning an error for everything that happens to be
         | a logic error at a given level of abstraction.
        
         | hansvm wrote:
         | Try-things have the benefit of accurately representing the
         | thing you're describing. Leave it to the caller to decide
         | whether to panic or resize the data structure or whatever.
         | 
         | That's also not the only choice in the design space for correct
         | array accesses. Instead of indices being raw integers, you can
         | use tagged types (in Rust, probably using lifetimes as the
         | mechanism if you had to piggy back on existing features, but
         | that's an implementation detail) and generate safe, tagged
         | indices which allow safe access without having to bounds check
         | on access.
         | 
         | However you do it, the point is to not lie about what you're
         | actually doing and invoke a panic-handler-something as a cludgy
         | way of working around the language.
        
       | sgt wrote:
       | Golang might be better for writing robust software, if that is
       | the goal. Robust services that don't go down.
        
         | NoTeslaThrow wrote:
         | Hell, just go full erlang at that point and you get loads of
         | services for "free".
        
         | efnx wrote:
         | And why is that? I already don't agree, but I'd love to hear
         | your take.
        
           | surajrmal wrote:
           | Mentioning golang is a rust article comment section is just
           | bait. People just live comparing the two even though it's
           | somewhat boring comparison
        
         | pjmlp wrote:
         | First they have to improve the memory model due to possible
         | races when sharing slices due to their fat pointers
         | implementation.
        
         | neillyons wrote:
         | Golang will panic with a runtime error index out of range if
         | you index out of bounds. There doesn't seem to be a nice built
         | in way to do `arr.get(3)` like in Rust.                   slice
         | := []int{1, 2, 3}         i := slice[3]         fmt.Println(i)
        
         | IshKebab wrote:
         | I don't think so. Rust has much stronger typing than Go which
         | allows you to prevent more classes of bugs than just memory
         | errors.
         | 
         | The coolest one I've heard is that Fuchsia's network stack
         | managed to eliminate deadlocks.
         | 
         | But even on a basic level Rust has that "if it compiles it
         | works" experience which Go definitely doesn't.
        
           | Mond_ wrote:
           | > The coolest one I've heard is that Fuchsia's network stack
           | managed to eliminate deadlocks.
           | 
           | Is there a write up on this? That's very cool
        
             | diarrhea wrote:
             | IIRC it is just having locks with exclusive constructors,
             | which take previous locks' guards (by ownership?).
             | 
             | That way you can never lock lock B if you have not received
             | a guard aka lock from lock A prior. Ensured on the type
             | level.
             | 
             | I suppose doing this at scale is a real challenge.
        
             | aw1621107 wrote:
             | I think that example comes from the talk "Safety in an
             | Unsafe World" [0, slides at 1].
             | 
             | There are some crates which implement lock ordering as well
             | (e.g., [2, 3]). lock-ordering states it's inspired by the
             | technique discussed in the talk as well, for what it's
             | worth.
             | 
             | [0]: https://youtu.be/qd3x5MCUrhw?t=1001 (~16:41 in case
             | the timestamp link doesn't work)
             | 
             | [1]: https://joshlf.com/files/talks/Safety%20in%20an%20Unsa
             | fe%20W... (deadlock prevention example starting slide 50)
             | 
             | [2]: https://github.com/akonradi/lock-ordering
             | 
             | [3]: https://github.com/alaric/lock_order
        
         | adhamsalama wrote:
         | That's a weird thing to say about a language that doesn't have
         | null safety.
        
           | int_19h wrote:
           | Not to be outdone here, Go introduces _multiple_ null values
           | that are considered distinct, yet they all exhibit the same
           | problem.
        
         | apatheticonion wrote:
         | Both have their place but after writing both extensively, I
         | much prefer Rust - despite the pitfalls.
         | 
         | My biggest critisim of Rust (in comparison to Go) is the lack
         | of a powerful standard library while Go's standard library is
         | outstanding. I would also like to see standardized interfaces
         | in Rust (like AsyncWrite) and in general the async story could
         | be better - though I appreciate the versatility.
        
       | forrestthewoods wrote:
       | > Overflow errors can happen pretty easily
       | 
       | No they can't. Overflows aren't a real problem. Do not add
       | checked_mul to all your maths.
       | 
       | Thankfully Rust changed overflow behavior from "undefined" to
       | "well defined twos-complement".
        
         | LiamPowell wrote:
         | What makes you think this is the case?
         | 
         | Having done a bunch of formal verification I can say that
         | overflows are probably the most common type of bug by far.
        
           | imtringued wrote:
           | Yeah, they're so common they've become a part of our culture
           | when it comes to interacting with computers.
           | 
           | Arithmetic overflows have become the punchline of video game
           | exploits.
           | 
           | Unsigned underflow is also one of the most dangerous types.
           | You go from one of the smallest values to one of the biggest
           | values.
        
         | conradludgate wrote:
         | Overflow errors absolutely do happen. They're just no longer
         | UB. It doesn't make them non-errors though. If your bank
         | account balance overflowed, you'd be pretty upset.
        
           | bogeholm wrote:
           | On the other hand, there's a solid use case for underflow.
        
         | int_19h wrote:
         | The vast majority of code that does arithmetic will not produce
         | a correct result with two's complement. It is simply assuming
         | that the values involved are small enough that it won't matter.
         | Sometimes it is a correct assumption, but whenever it involves
         | anything derived from inputs, it can go very wrong.
        
           | zozbot234 wrote:
           | For any arithmetic expression that involves only + - *
           | operators and equally-sized machine words, two's complement
           | will actually yield a "correct" result. It's just that the
           | given result might be indicating a different range than you
           | expect.
        
         | wongarsu wrote:
         | I'm a big fan of liberal use of saturating_mul/add/sub whenever
         | there is a conceivable risk of coming withing a couple orders
         | of magnitude of overflow. Or checked_*() or whatever the best
         | behavior in the given case is. For my code it happens to mostly
         | be saturating.
         | 
         | Overflow bugs are a real pain, and so easy to prevent in Rust
         | with just a function call. It's pretty high on my list of
         | favorite improvements over C/C++
        
       | thrance wrote:
       | I'd add memory leaks to the list. Sometimes you feel compelled to
       | wrap your data in an Rc or Arc (reference counted pointers for
       | those unfamiliar) to appease the borrow checker. With capture
       | semantics of closures and futures and such it's quite easy to
       | fall into a referential cycle, which won't be freed when dropped.
        
       | Sharlin wrote:
       | _> Surprising Behavior of Path::join With Absolute Paths
       | 
       | > I was not the only one who was confused by this behavior.
       | Here's a thread on the topic, which also includes an answer by
       | Johannes Dahlstrom:
       | 
       | > > The behavior is useful because a caller [...] can choose
       | whether it wants to use a relative or absolute path, and the
       | callee can then simply absolutize it by adding its own prefix and
       | the absolute path is unaffected which is probably what the caller
       | wanted. The callee doesn't have to separately check whether the
       | path is absolute or not.
       | 
       | > And yet, I still think it's a footgun. It's easy to overlook
       | this behavior when you use user-provided paths. Perhaps join
       | should return a Result instead? In any case, be aware of this
       | behavior._
       | 
       | Oh, hey, that's me! I agree that it's a footgun, for what it's
       | worth, and there should probably get a dedicated "absolutize"
       | method for getting the "prefix this if relative, leave as is if
       | already absolute" semantics.
       | 
       | (link to the thread: https://users.rust-lang.org/t/rationale-
       | behind-replacing-pat...)
        
         | glandium wrote:
         | It's the same in at least Python, so it's not a Rust
         | idiosyncratic behavior. The "absolutize" method you're asking
         | for exists since 1.79. https://doc.rust-
         | lang.org/stable/std/path/fn.absolute.html
        
       | conaclos wrote:
       | I find it strange that the article doesn't talk about the
       | alternative to checked arithmetic: explicit Wrapping [0] and
       | Saturating [1] types, also provided as methods on numeric types
       | (e.g. `usize::MAX.saturating_add(1)`).
       | 
       | Regarding `as` casting, I completely agree. I am trying to use
       | safe `From::from` instead. However, this is a bit noisy:
       | `usize::from(n)` vs `n as usize`.
       | 
       | [0] https://doc.rust-lang.org/std/num/struct.Wrapping.html [1]
       | https://doc.rust-lang.org/std/num/struct.Saturating.html
        
         | kelnos wrote:
         | > _I am trying to use safe `From::from` instead. However, this
         | is a bit noisy: `usize::from(n)` vs `n as usize`._
         | 
         | If there's enough information in the surrounding code for type
         | inference to do its thing, you can shorten it to `n.into()`.
        
       ___________________________________________________________________
       (page generated 2025-04-06 23:00 UTC)