Remix.run Logo
amluto 14 hours ago

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.

kibwen 14 hours ago | parent | next [-]

> 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 11 hours ago | parent [-]

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.

conradludgate 14 hours ago | parent | prev | next [-]

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.

yuriks 14 hours ago | parent | prev | next [-]

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 14 hours ago | parent [-]

> 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 13 hours ago | parent [-]

> 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.

14 hours ago | parent | prev [-]
[deleted]