[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)