[HN Gopher] The Algebra of Loans in Rust
       ___________________________________________________________________
        
       The Algebra of Loans in Rust
        
       Author : g0xA52A2A
       Score  : 177 points
       Date   : 2025-12-22 19:25 UTC (4 days ago)
        
 (HTM) web link (nadrieril.github.io)
 (TXT) w3m dump (nadrieril.github.io)
        
       | the__alchemist wrote:
       | I have what I thought was a broad knowledge base of rust an
       | experience in it over many domains, but I haven't heard of most
       | of those. Have been getting by with `&`, and `&mut` only from
       | those tables!
       | 
       | Incidentally, I think this is one of Rust's best features, and I
       | sorely miss it in Python, JS and other languages. They keep me
       | guessing whether a function will mutate the parent structure, or
       | a local copy in those languages!
       | 
       | Incidentally, I recently posted in another thread here how I just
       | discovered the 'named loop/scope feature, and how I thought it
       | was great, but took a while to discover. A reply was along the
       | effect of "That's not new; it's a common feature". Maybe I don't
       | really know rust, but a dialect of it...
        
         | mring33621 wrote:
         | I'm just learning Rust but so far, it looks like the author is
         | proposing some of these ref types, like &own and &uninit.
         | 
         | I don't know 100% for sure. It's a bit confusing...
        
           | jojomodding wrote:
           | The part of the blog post where it says
           | 
           | > What's with all these new reference types? > All of these
           | are speculative ideas
           | 
           | makes it pretty clear to me that they are indeed not yet part
           | of Rust but instead something people have been thinking about
           | adding. The rest of the post discusses how these would work
           | if they were implemented.
        
           | whytevuhuni wrote:
           | Right. The &pin, &own, and &uninit in the article (or rather
           | everything except & and &mut in that table) do not exist in
           | Rust.
           | 
           | I have seen &pin being proposed recently [1], first time I'm
           | seeing the others.
           | 
           | [1] https://blog.rust-lang.org/2025/11/19/project-goals-
           | update-o...
        
             | VorpalWay wrote:
             | Own and uninit have been in discussions wrt in place
             | construction. The Rust in the Linux kernel project seems to
             | be the motivating use case for this that really got the
             | effort going recently.
        
         | GardenLetter27 wrote:
         | Rust gives you no guarantees that a function won't allocate or
         | panic though.
        
           | MaulingMonkey wrote:
           | This is something I do wish Rust could better support. A
           | `#![no_std]` library crate can at least discourage allocation
           | (although it can always `extern crate alloc;` in lib.rs or
           | invoke malloc via FFI...)
        
             | maxbond wrote:
             | Is the juice worth the squeeze to introduce two new
             | function colors? What would you do if you needed to call
             | `unreachable!()`?
             | 
             | It's a shame that you can't quite do this with a lint,
             | because they can't recurse to check the definitions of
             | functions you call. That would seem to me to be ideal,
             | maintain it as an application-level discipline so as not to
             | complicate the base language, but automate it.
        
               | MaulingMonkey wrote:
               | > Is the juice worth the squeeze to introduce two new
               | function colors?
               | 
               | Typically no... which is another way of saying
               | occasionally yes.
               | 
               | > What would you do if you needed to call
               | `unreachable!()`?
               | 
               | Probably one of e.g.:                   unsafe {
               | core::hint::unreachable_unchecked() }         loop {}
               | 
               | Which are of course the wrong habits to form! (More
               | seriously: in the contexts where such no-panic colors
               | become useful, it's because you need to _not_ call
               | `unreachable!()`.)
               | 
               | > It's a shame that you can't quite do this with a lint,
               | because they can't recurse to check the definitions of
               | functions you call. That would seem to me to be ideal,
               | maintain it as an application-level discipline so as not
               | to complicate the base language, but automate it.
               | 
               | Indeed. You can mark a crate e.g. #![deny(clippy::panic)]
               | and isolate that way, but it's not quite the rock solid
               | guarantees Rust typically spoils us with.
        
               | VorpalWay wrote:
               | > Typically no... which is another way of saying
               | occasionally yes.
               | 
               | You might be able to avoid generating panic handling
               | landing pads if you know that a function does not call
               | panic (transitively). Inlining and LTO often help, but
               | there is no guarantee that it will be possible to elide,
               | it depends on the whims of the optimiser.
               | 
               | Knowing that panicking doesn't happen can also enable
               | other optimisations that wouldn't have been correct if a
               | panic were to happen.
               | 
               | All of that is _usually_ very minor, but in a hot loop it
               | could matter, and it will help with code size and
               | density.
               | 
               | (Note that this is assuming SysV ABI as used by everyone
               | except Windows, I have no clue how SEH exceptions on
               | Windows work.)
               | 
               | > Indeed. You can mark a crate e.g.
               | #![deny(clippy::panic)] and isolate that way, but it's
               | not quite the rock solid guarantees Rust typically spoils
               | us with.
               | 
               | Also, there are many things in Rust which can panic apart
               | from actual calls to panic or unwrap: indexing out of
               | bounds, integer overflow (in debug), various std
               | functions if misused, ...
        
           | VorpalWay wrote:
           | Yes that is annoying, but I don't know of any mainstream
           | systems language that does. C and C++ can also have
           | allocations anywhere, and C++ have exceptions. And those are
           | really the only competitors to Rust for what I do (hard
           | realtime embedded).
           | 
           | Zig might be an option in the future, and it does give more
           | control over allocations. I don't know what the exception
           | story is there, and it isn't memory safe and doesn't have
           | RAII so I'm not that interested myself at this point.
           | 
           | I guess Ada could be an option too, but I don't know nearly
           | enough about it to say much.
        
             | jibal wrote:
             | Zig doesn't have exceptions, it has error unions, so
             | basically functions return either a value or an error code
             | and the caller is forced by the language to note which was
             | returned. And instead of RAII it has defer ... which of
             | course can easily be forgotten or mis-scoped, so it's not
             | safe.
        
             | gethly wrote:
             | For allocation, Zig and Odin. Zig is explicit and Odin is
             | implicit.
        
               | prxm wrote:
               | > Zig is explicit
               | 
               | i never got this point. whats stopping me from writing a
               | function like this in zig?                 fn
               | very_bad_func() !i32 {           var GPA =
               | std.heap.GeneralPurposeAllocator(.{}){};           var
               | gpa = GPA.allocator();           var s = try
               | gpa.alloc(i32, 1000);           s[0] = 7;
               | return s[0];       }
               | 
               | the only thing explicit about zig approach is having
               | ready-to-use allocator definitons in the std library. if
               | you excluded std library and write your own allocators,
               | you could have an even better api in rust compared to zig
               | thanks to actual shared behaviour features (traits).
               | explicit allocation is a library feature, not a language
               | feature.
        
               | gethly wrote:
               | the explicit part is that zig forces you to import
               | allocator of your choosing whereas odin has allocator
               | passed as part of hidden context and you can
               | change/access it only if you want to. hence explicit
               | behavior vs implicit behavior.
               | 
               | i use neither of those languages, so don't ask me for
               | technical details :D
        
               | thegeekpirate wrote:
               | You can require allocations in Odin to be explicit using
               | `#+vet explicit-allocators`
        
             | FpUser wrote:
             | >"Yes that is annoying, but I don't know of any mainstream
             | systems language that does. C and C++ can also have
             | allocations anywhere, and C++ have exceptions."
             | 
             | C++ has a way to tell to compiler that the function would
             | raise no exceptions. Obviously it is not a guarantee that
             | at runtime exception will not happen. In that case the
             | program would just terminate. So it is up to a programmer
             | to turn on some brain activity to decide should they mark
             | function as one or not.
        
         | goku12 wrote:
         | I doubt that anybody truly knows Rust. And this is aggravated
         | by the fact that features keep getting added. But here are two
         | simple strategies that I found very effective in keeping us
         | ahead of the curve.
         | 
         | 1. Always keep the language reference with you. It's absolutely
         | not a replacement for a good introductory textbook. But it's an
         | unusually effective resource for anybody who has crossed that
         | milestone. It's very effective in spontaneously uncovering new
         | language features and in refining your understanding of the
         | language semantics.
         | 
         | What we need to do with it is to refer it occasionally for even
         | constructs that you're familiar with - for loops, for example.
         | I wish that it was available as auto popups in code editors.
         | 
         | 2. Use clippy, the linter. I don't have much to add here. Your
         | code will work without it. But for some reason, clippy is an
         | impeccable tutor into idiomatic Rust coding. And you get the
         | advantage of the fact that it stays in sync with the latest
         | language features. So it's yet another way to keep yourself
         | automatically updated with the language features.
        
           | VorpalWay wrote:
           | I feel like other languages also have the issue of complexity
           | and changing over time. I doubt I know all of C++ post C++14
           | for example (even though that is my day job). Keeping up with
           | all the things they throw into the standard library of Python
           | is also near impossible unless you write python every day.
           | 
           | Rust has an unusually short release cycle, but each release
           | tends to have fewer things in it. So that is probably about
           | the same when it comes to new features per year in Python or
           | C++.
           | 
           | But sure, C moves slower (and is smaller to begin with). If
           | that is what you want to compare against. But all the
           | languages I work with on a daily basis (C++, Python and Rust)
           | are sprawling.
           | 
           | I don't have enough experience to speak about other languages
           | in depth, but as I understand it Haskell for example has a
           | lot of extensions. And the typescript/node ecosystem seems to
           | move crazy fast and require a ton of different moving pieces
           | to get anything done (especially when it comes to the build
           | system with bundlers, minifiers and what not).
        
             | jacquesm wrote:
             | Languages should be small, not large. I find that every
             | language I've ever used that tries to throw everything and
             | the kitchensink at you eventually deteriorates into a mess
             | that spills over into the projects based on that language
             | in terms of long term instability. You should be able to
             | take a 10 year old codebase, compile it and run it.
             | Backwards compatibility is an absolute non-negotiable for
             | programming languages and if you disagree with that you are
             | building toys, not production grade systems.
        
               | aw1621107 wrote:
               | > Backwards compatibility is an absolute non-negotiable
               | for programming languages
               | 
               | What programming language(s) satisfy this criteria, if
               | any?
        
               | GhosT078 wrote:
               | Ada does. It has been through 5 editions so far and
               | backwards compatibility is always maintained except for
               | some small things that are documented and usually easy to
               | update.
        
               | aw1621107 wrote:
               | I'd normally be inclined to agree that minor things are
               | probably good enough, but "absolute non-negotiable" is a
               | rather strong wording and i think small things
               | technically violate a facial reading, at least.
               | 
               | On the other hand, I did find what I think are the
               | relevant docs [0] while looking more into things, so I
               | got to learn something!
               | 
               | [0]: https://docs.adacore.com/gnat_rm-
               | docs/html/gnat_rm/gnat_rm/c...
        
               | cogman10 wrote:
               | > except for some small things that are documented
               | 
               | I can't think of any established language that doesn't
               | fit that exact criteria.
               | 
               | The last major language breakage I'm aware of was either
               | the .Net 2 to 3 or Python 2 to 3 changes (not sure which
               | came first). Otherwise, pretty much every language that
               | makes a break will make it in a small fashion that's well
               | documented.
        
               | gethly wrote:
               | Go, PHP, Ruby, JavaScript ... I'd say majority, actually.
        
               | aw1621107 wrote:
               | It's probably borderline due to the opt-in mechanism, but
               | Go did make a technically backwards-incompatible change
               | to how its for loops work in 1.22 [0].
               | 
               | PHP has had breaking changes [1].
               | 
               | Ruby has had breaking changes [2] (at the very least
               | under "Compatibility issues")
               | 
               | Not entirely sure whether this counts, but ECMAScript has
               | had breaking changes [3].
               | 
               | [0]: https://go.dev/blog/loopvar-preview
               | 
               | [1]: https://www.php.net/manual/en/migration80.incompatib
               | le.php
               | 
               | [2]: https://www.ruby-
               | lang.org/en/news/2025/12/25/ruby-4-0-0-rele...
               | 
               | [3]: https://tc39.es/ecma262/2025/#sec-additions-and-
               | changes-that...
        
               | gethly wrote:
               | There is no such thing as perfection in the real world.
               | Close enough is good enough.
        
               | aw1621107 wrote:
               | I'd normally agree with you in practice, but since "close
               | enough" seems likely to cover most mainstream languages
               | in use today I figured "absolute non-negotiable" probably
               | was intended to mean a stricter standard.
        
               | kbolino wrote:
               | The interesting thing about Go's loopvar change is that
               | nobody was able to demonstrate any real-world code that
               | it broke (*1), while several examples were found of real-
               | world code (often tests) that it _fixed_ (*2).
               | Nevertheless, they gated it behind go.mod specifying a go
               | version  >= 1.22, which I personally think is overly
               | conservative.
               | 
               | *1: A great many examples of synthetic code were
               | contrived to argue against the change, but none of them
               | ever corresponded to Go code anyone would actually write
               | organically, and an extensive period of investigation
               | turned up nothing
               | 
               | *2: As in, the original behavior of the code was actually
               | incorrect, but this wasn't discovered until after the
               | loopvar change caused e.g. some tests to fail, prompting
               | manual review of the relevant code; as a tangent, this
               | raises the question of how often tests just conform to
               | the code rather than the other way around
        
               | aw1621107 wrote:
               | You certainly won't find me arguing against that change,
               | and the conservatism is why I called it borderline. The
               | only reason I bring it up is because of the "absolute
               | non-negotiable" bit, which I took to probably indicate a
               | very exacting standard lest it include most widespread
               | languages anyways.
        
               | kbolino wrote:
               | Yes, I think it's also a good example of how "absolute"
               | backwards compatibility is not necessarily a good thing.
               | Not only was the old loopvar behavior probably the
               | biggest noob trap in Go (*), it turned out not to be what
               | _anyone_ writing Go code in the wild actually wanted,
               | even people experienced with the language. Everyone seems
               | to have: a) assumed it always worked the way it does now,
               | b) wrote code that wasn 't sensitive to it in the first
               | place, or c) worked around it but never benefitted from
               | it.
               | 
               | *: strongest competitor for "biggest noob trap" IMO is
               | using defer in a loop/thinking defer is block scoped
        
               | aw1621107 wrote:
               | Strongly agree there. IMO breaking backwards
               | compatibility is a tradeoff like any other, and the
               | flexibility non-hardline stances give you is handy for
               | real-world situations,
        
               | SideburnsOfDoom wrote:
               | Yes, most of them.
               | 
               | C# for instance isn't such a "small language", it has
               | grown, but code from older versions, that does not use
               | the newer features will almost always compile and work as
               | before.
               | 
               | breaking changes are for corner cases, e.g. https://githu
               | b.com/dotnet/roslyn/blob/main/docs/compilers/CS...
        
               | aw1621107 wrote:
               | The thing is that "most of them" seems incongruous with a
               | demand for "absolute non-negotiable" backwards
               | compatibility. If not for that particular wording I
               | probably wouldn't have said anything.
        
               | speed_spread wrote:
               | Java rules here. You can take any Java 1.0 (1995)
               | codebase and compile it as-is on a recent JDK. Moreover,
               | you can also use any ancient compiled Java library and
               | link it to modern Java app. Java source and bytecode
               | backward compatibility is fantastic.
        
               | aw1621107 wrote:
               | Java has had some breaking changes (e.g., [0, 1]), though
               | in practice I have to say my experience tends to agree
               | and I've been fortunate enough to never run into issues.
               | 
               | [0]: https://stackoverflow.com/q/1654923
               | 
               | [1]: https://news.ycombinator.com/item?id=28542853
        
               | cogman10 wrote:
               | * Terms and conditions apply
               | 
               | Java is very good here, but (and not totally it's fault)
               | it did expose internal APIs to the userbase which have
               | caused a decent amount of heartburn. If your old codebase
               | has a route to `sun.misc.unsafe` then you'll have more of
               | a headache making an upgrade.
               | 
               | Anyone that's been around for a while and dealt with the
               | 8->9 transition has been bit here. 11->17 wasn't without
               | a few hiccups. 17->21 and 21->25 have been uneventful.
        
               | VorpalWay wrote:
               | Rust does. You have editions to do breaking changes at
               | the surface level. But that is per crate (library) and
               | you can mix and match crates with different editions
               | freely.
               | 
               | Thry do reserve the right to do breaking changes for
               | security fixes, soundness fixes and inference changes
               | (i.e. you may need to add an explicit type that was
               | previously inferred but is now ambiguous). These are
               | quite rare and usually quite small.
        
               | aw1621107 wrote:
               | I'd normally agree that what you say is good enough in
               | practice, but I question whether it meets GP's "absolute
               | non-negotiable" standards. That specific wording is the
               | reason I asked the question in the first place; it seemed
               | to me that there was some standard that apparently wasn't
               | being met and I was wondering where exactly the bar was.
        
               | kreetx wrote:
               | Haskell's user-facing language gets compiled down to
               | Haskell "core" which is what the language actually _can
               | do_. So any new language feature has a check in with
               | sanity when that first transformation gets written.
        
               | kibwen wrote:
               | I'm not sure what this is arguing against here. Anyone
               | who follows Rust knows that it's relatively modest when
               | it comes to adding new features; most of the "features"
               | that get added to Rust are either new stdlib APIs or just
               | streamlining existing features so that they're less
               | restrictive/easier to use. And Rust has a fantastic
               | backwards compatibility story.
        
               | armchairhacker wrote:
               | I suspect the problem is that every feature makes it
               | possible for an entire class of algorithms to be
               | implement much more efficiently and/or clearly with a
               | small extension to the language.
               | 
               | Many people encounter these algorithms after many other
               | people have written large libraries and codebases. It's
               | much easier to slightly extend the language than start
               | over or (if possible) implement the algorithm in an ugly
               | way that uses existing features. But enough extensions
               | (and glue to handle when they overlap) and even a
               | language which was initially designed to be simple, is no
               | longer.
               | 
               | e.g., Go used to be much simpler. But in particular, lack
               | of generics kept coming up as a pain point in many
               | projects. Now Go has generics, but arguably isn't simple
               | anymore.
        
               | kstrauser wrote:
               | Egad, no. This is how you get C++, whose core tenet seems
               | to be "someone used this once in 1994 so we can never
               | change it".
               | 
               | Even adding a new keyword will break some code out there
               | that used that as a variable name or something. Perfect
               | backward compatibility means you can never improve
               | anything, ever, lest it causes someone a nonzero amount
               | of porting effort.
        
               | Someone wrote:
               | If you want or have to build a large program, something
               | must be large, be it the language, its standard library,
               | third party code, or code you write.
               | 
               | I think it's best if it is one of the first two, as that
               | makes it easier to add third party code to your code, and
               | will require less effort to bring newcomers up to speed
               | w.r.t. the code. As an example, take strings. C doesn't
               | really have them as a basic type, so third party
               | libraries all invent their own, requiring those using
               | them to add glue code.
               | 
               | That's why standard libraries and, to a lesser extent,
               | languages, tend to grow.
               | 
               | Ideally that's with backwards compatibility, but there's
               | a tension between moving fast and not making mistakes, so
               | sometimes, errors are made, and APIs 'have' to be
               | deprecated or removed.
        
               | rayiner wrote:
               | > Languages should be small, not large.
               | 
               | Yes. At the very least, features should carry a lot of
               | weight and be orthogonal to other features. When I was
               | young I used to pride myself on knowing all the ins and
               | outs of modern C++, but over time I realized that needing
               | to be a "language lawyer" was a design shortcoming.
               | 
               | All that being said I've never seen the functionality of
               | Rust's borrow checker reduced to a simpler set of
               | orthogonal features and it's not clear that's even
               | possible.
        
             | pjmlp wrote:
             | Even C, we are now at C23, and I bet most folks only know
             | "my compiler C", and not even all the extensions it offers.
        
               | esafak wrote:
               | That's why it's important to get it right early in the
               | language. The new stuff may as well not exist.
        
           | gucci-on-fleek wrote:
           | I don't know Rust at all, but all your comments
           | 
           | > I doubt that anybody truly knows <language>.
           | 
           | > Always keep the language reference with you.
           | 
           | > Use <tool>, the linter.
           | 
           | seem like they apply to _all_ languages (and I agree that
           | they 're great advice!).
        
         | the8472 wrote:
         | > All of these are speculative ideas, but at this point they've
         | been circulating a bunch so should be pretty robust.
        
         | VorpalWay wrote:
         | Many of the things like "&own" are ideas being discussed, they
         | don't exist in the language yet. As far as I know only &, &mut
         | and raw pointers (mut and const) exist in stable rust at this
         | point. The standard library has some additional things like
         | NonNull, Rc, etc.
        
         | jibal wrote:
         | > I sorely miss it in Python, JS and other languages. They keep
         | me guessing whether a function will mutate the parent
         | structure, or a local copy in those languages!
         | 
         | Python at least is very clear about this ... everything, lists,
         | class instances, dicts, tuples, strings, ints, floats ... are
         | all passed by object reference. (Of course it's not relevant
         | for tuples and scalars, which are immutable.)
        
           | mrkeen wrote:
           | Everything being passed by object reference just means every
           | case is equally unclear.                 answer =
           | frobnicate(foo)
           | 
           | Will frobnicate destroy foo or not?
        
             | kstrauser wrote:
             | No. It can't. It can only destroy its own reference to foo,
             | not the calling scope's reference.
        
               | mrkeen wrote:
               | Right, but I don't care about the _reference to foo_
               | (that 's a low-level detail that should be confined to
               | systems languages, not application languages) I was
               | asking about the _foo_.
        
               | kstrauser wrote:
               | Right, but that reference is all the function has. It
               | can't destroy another scope's reference to the foo, and
               | the Python GC won't destroy the foo as long as a
               | reference to it exists.
               | 
               | The function could mutate foo to be empty, if foo is
               | mutable, but it can't make it not exist.
        
               | mrkeen wrote:
               | >> I sorely miss it in Python, JS and other languages.
               | They keep me guessing whether a function will mutate the
               | parent structure, or a local copy in those languages!
               | 
               | No mention of references!
               | 
               | I don't care about references to foo. I don't care about
               | facades to foo. I don't care about decorators of foo. I
               | don't care about memory segments of foo.
               | 
               | "Did someone eat my lunch in the work fridge?"
               | 
               | "Well at least you wrote your name in permanent marker on
               | your lunchbox, so that should help narrow it down"
        
               | kstrauser wrote:
               | Then I don't know what you mean. If you have:
               | foo = open('bar.txt')       answer = frobnicate(foo)
               | print(foo)
               | 
               | then frobnicate may call foo.close(), or it may read
               | foo's contents so that you'd have to seek back to the
               | beginning before you could read them a second time.
               | There's literally nothing you can do in frobnicate that
               | can make it such that the 3rd raises a NameError because
               | foo no longer exists.
        
         | kibwen wrote:
         | _> Incidentally, I recently posted in another thread here how I
         | just discovered the  'named loop/scope feature, and how I
         | thought it was great, but took a while to discover. A reply was
         | along the effect of "That's not new; it's a common feature".
         | Maybe I don't really know rust, but a dialect of it..._
         | 
         | I assume I'm the one who taught you this, and for the
         | edification of others, you can do labeled break not only in
         | Rust, but also C#, Java, and JavaScript. An even more powerful
         | version of function-local labels and break/continue/goto is
         | available in Go (yes, in Go!), and a yet more powerful version
         | is in C and C++.
         | 
         | The point being, the existence of obscure features does not a
         | large or complex language make, unless you're willing to call
         | Go a large and complex language. By this metric, anyone who's
         | never used a goto in Go is using a dialect of Go, which would
         | be silly; just because you've never had cause to use a feature
         | of a language does not a dialect make.
        
           | morcus wrote:
           | Wow, I had no idea JavaScript has labeled break! Thanks for
           | the comment.
        
         | nextaccountic wrote:
         | Of that table, only & and &mut actually exist, the rest are
         | hypothetical syntax
        
         | Sytten wrote:
         | Another one that is missing in the article is &raw mut/const
         | but it is purely for unsafe usage when you need a pointer to an
         | unaligned field of a struct.
        
           | steveklabnik wrote:
           | &raw T/&raw mut T aren't pointer types, they're syntax for
           | creating *const T/*mut T.
           | 
           | These aren't included in the article because they are not
           | borrow checked, but you're right that if someone was trying
           | to cover 100% of pointer types in Rust, raw pointers would be
           | missing.
        
             | gpm wrote:
             | If we start creating more `&... T` ptr types I wonder if a
             | future edition changes `*const T` to `&raw T` for
             | consistency...
        
               | kibwen wrote:
               | I'm hoping that languages move away from the sigil-ified
               | legacy of C treating pointers as special syntax and just
               | start calling these `Ref<T>`, `PtrConst<T>`, etc.
        
               | steveklabnik wrote:
               | Ehhh it's already consistent: ones with & borrow, ones
               | without do not.
        
         | sesm wrote:
         | TypeScript has `Readonly<T>` for this purpose.
        
         | FpUser wrote:
         | >"...and other languages"
         | 
         | Many "other languages", particularly ones that compile to
         | native code in traditional way have fairly explicit ways of
         | specifying how said parameters to be treated
        
       | amluto wrote:
       | Some things I occasionally contemplate: (not that I've ever tried
       | to work any of these out anywhere near completely)
       | 
       | 1. Why isn't there a variant of &mut that doesn't allow swapping
       | the value? I feel like it ought to be possible to lend out
       | permission to mutate some object but not to replace it. Pinning
       | the object works, but that's rather extreme.
       | 
       | 2. Would it be safe to lend the reference type above to a pinned
       | object? After all, if a function promises to return with the
       | passed-in parameter intact in its original location and not to
       | swap it with a different value/place, then its address must stay
       | intact.
       | 
       | 3. Why is pinning a weird sticky property of a reference?
       | Shouldn't non-movability of an object be a property of the
       | object's type? Is it just a historical artifact that it works the
       | way it does or is this behavior actually desirable?
       | 
       | 4. Wouldn't it be cool if there was a reference type that gave no
       | permissions at all but still guaranteed that the referred-to
       | object would continue to exist? It might make more sense to use
       | with RefCell-like objects than plain &. This new reference type
       | could exist concurrently with &mut.
        
         | yuriks wrote:
         | For 1, I think it's hard to make a distinction between swapping
         | an object, vs. swapping/mutating all of its fields such that it
         | becomes equivalent to a different object.
         | 
         | For 3, some objects only need to be pinned under certain
         | circumstances, e.g. futures only need to be pinned after
         | they're polled for the first time, but not before. So it's
         | convenient to separate the pinnability property to allow them
         | to be moved freely beforehand.
         | 
         | I don't quite understand the usecase you have in mind for 4.
        
           | amluto wrote:
           | > For 1, I think it's hard to make a distinction between
           | swapping an object, vs. swapping/mutating all of its fields
           | such that it becomes equivalent to a different object.
           | 
           | Privacy. If an object has fields I can't access, but I have
           | an &mut reference, I can indirectly modify them by swapping
           | the object.
           | 
           | More generally, there are a handful of special-seeming things
           | one can do to an object: dropping it, swapping it, forgetting
           | it, and leaking it. Rust does not offer especially strong
           | controls for these except for pinned objects, and even then
           | it feels like the controls are mostly a side effect of
           | pinning.
           | 
           | > For 3, some objects only need to be pinned under certain
           | circumstances, e.g. futures only need to be pinned after
           | they're polled for the first time, but not before.
           | 
           | Is this actually useful in practice? (This is a genuine
           | question, not a rhetorical question. But maybe let's pretend
           | that Rust had the cool ability to farm out initialization if
           | uninitialized objects described in the OP: allowing access
           | before pinning sounds a bit like allowing references to
           | uninitialized data before initializing it.)
           | 
           | For #4, I'm not sure I have a real use case. Maybe I'll try
           | contemplating a bit more. Most I think that shared ^
           | exclusive is a neat concept but that maybe there's room to
           | extend it a little bit, and there isn't any fundamental
           | reason that a holder of an &mut reference needs to ensure
           | that no one else can even identify the object while the &mut
           | reference is live.
        
             | yuriks wrote:
             | > Is this actually useful in practice?
             | 
             | It's required to do any intialization, particularly for
             | compound futures (e.g. a "join" or "select" type of
             | combinator), since you need to be able to move the future
             | from where it's created to where it's eventually
             | used/polled. I assume some of those cases could be subsumed
             | by &uninit if that existed yeah.
        
         | conradludgate wrote:
         | Common wisdom is that pinning is a property of the place, not
         | the reference or the type.
         | 
         | A type that might require stable pointers, like async{}, might
         | want to be movable prior to use, so you don't want the type to
         | require the value be pinned immediately. Or if you do, you need
         | a construction like pinned-init that offers `&pin out T` - a
         | pinned place that can be written to on initialisation of the
         | type.
        
         | kibwen wrote:
         | _> Why isn't there a variant of  &mut that doesn't allow
         | swapping the value?_
         | 
         | This is a very insightful observation, and Niko Matsakis
         | (leading influence of Rust's borrow checker) would likely agree
         | with you that this is an instance where Rust's default
         | borrowing rules are probably too permissive, in the sense that
         | being more restrictive by default regarding the "swappability"
         | of &mut could lead to Rust being able to provide more
         | interesting static guarantees. See his blog post here:
         | https://smallcultfollowing.com/babysteps/blog/2024/09/26/ove...
         | 
         |  _> Why is pinning a weird sticky property of a reference?
         | Shouldn't non-movability of an object be a property of the
         | object's type?_
         | 
         | See this blog post from withoutboats:
         | https://without.boats/blog/pinned-places/ for arguments as to
         | why pinning is properly modeled as a property of a place rather
         | than a type (particularly the section "Comparison to immovable
         | types"), as well as this post from Niko that ties this point in
         | with the above point regarding swappability:
         | https://smallcultfollowing.com/babysteps/blog/2024/10/14/ove...
        
           | oconnor663 wrote:
           | Yes I'm especially interested in what OP thinks about the
           | overlap (or not?) between the ideas in this post and the
           | ideas in this part of boats' post:
           | 
           | > One could imagine an alternative design in which instead of
           | places being unpinned by default and opting into pinning,
           | places are pinned (or perhaps "immovable") by default, and
           | have to opt into supporting the ability to move out of them.
           | This would make it so that by default places have the least
           | power (can only access via shared reference) and they gain a
           | monotonically increasing set of powers (can assign to them,
           | can move out of them).
           | 
           | > In addition to places having to opt into moving, there
           | would be three reference types instead of two: immutable,
           | mutable, and movable references.
        
       | asciii wrote:
       | I was thinking this is some accounting primer in Rust...dang,
       | still good stuff to know.
        
       | meisel wrote:
       | What does this sort of language complexity mean for future
       | changes in Rust? In C++, its existing complexity makes new
       | changes so much more difficult. Is Rust reaching a similar place?
        
         | kibwen wrote:
         | Can you be more specific? What language complexity are you
         | referring to? Plenty of things in this post are hypothetical,
         | not actual features being surfaced by the language.
        
       | trevortheblack wrote:
       | Am i mistaken or is there an error in the writing?
       | 
       | > For example, if I have a &own T I can reborrow it into a &mut T
       | but not a &pin own T.
       | 
       | From the table can't you do both? Maybe they mean "not a &pin mut
       | T" ?
        
       ___________________________________________________________________
       (page generated 2025-12-26 23:01 UTC)