I'm failing to confidently understand pinning. Why are pinned pointers expressed as e.g.Pin<Box<T>> or Pin<&mut T> instead of Box<Pin<T>> or &mut Pin<T>?
Intuitively, you'd think that the former pattern expresses an immovable pointer to movable data, and the latter pattern expresses a movable pointer to immovable data. AFAICT, that intuition is wrong (although it seems to confuse the LLMs as much as it confuses me). In fact, I believe the former pattern expresses a movable pointer to immovable data. So, how could I express an immovable pointer to movable data? Is this some weird syntax inconsistency where established "pointer types" get special treatment with regard to Pin<T>?
More context:
I need to implement a self-referential data structure. The structure looks vaguely like this:
The idea is that all of the objects are contained within that all_objects vector, and they can point to one another as long as they're all in the same collection. Yes, I tried implementing this with vector indices instead of pointers first, but it makes my algorithms way more complicated, so trust me that I actually want a self-referential type.
I believe I can see all the footguns lurking here. I know that I need to pre-allocate all the space the vector could possibly need on initialization with Vec::with_capacity, then only use push_within_capacity; never push. My understanding is that, as long as I'm extra careful to follow those rules, I don't really need to involve the Pin marker at all. The contents of the vector will not move.
But the docs even have a specific example where they discuss pinning the contents of a Vec, so I'm wondering if I should use something like Pin<Vec<Object>> or Vec<Pin<Object>> to express to readers "the contents of this vector should never move".
Values you hold without indirection are always movable. So if you have some variable with a Pin<Box<T>> in it, yes, you can move the Pin<_>... which will also move the Box<T> (but not the T).
Pin<_> is designed to be used with pointer types, so you pretty much always have a Pin<Ptr<_>>. Note how all its inherent methods involve Deref or DerefMut. Think of it as a modifier for pointer types.
Why do you want a pointer that can't move? I'm not seeing the motivation from your use case.
In my case, I need a vector of objects where each object in that vector never moves. It seemed like I might be able to use Pin to achieve a bit of type-system-level enforcement of that invariant, but I guess I'm learning that Pin<Vec<T>> just doesn't make sense in Rust (even though a Vec is basically just a pointer + a length, it's not considered a "pointer type").
I was only using the Box<Pin<T>> hypothetical type as a way to illustrate my confusion, because my instinct was to reach for Vec<Pin<T>>, but that doesn't make sense either (unless T is itself a pointer type).
Disregarding technical issues, which I think are all solvable, the logical issue is that Vec is fundamentally mutable - if you push a new item it might need to move all the items to a new location so it doesn't make sense.
You can instead use into Vec::into_boxed_slice and Pin that if you don't need to append any more items.
It’s not really that it‘s “not a pointer type”, but rather:
A basic characteristic of Vec is that it reallocates when items are added, so Pin<Vec<T>> “doesn’t make sense” in that it might as well be a Pin<Box<[T]>> instead.
In a lot of cases, how pinning should interact with standard library types hasn’t really been decided on, or even explored, and that's why Pin<Box<[T]>> is not very useful.
But none of this has to stop you from writing your own type which does have the properties you need. Here’s a quick sketch:
use std::pin::Pin;
pub struct PinBoxSlice<T>(Box<[T]>);
impl<T> PinBoxSlice<T> {
pub fn new(elements: Vec<T>) -> Self {
Self(Box::from(elements))
}
pub fn get_mut(&mut self, index: usize) -> Option<Pin<&mut T>> {
let element: &mut T = self.0.get_mut(index)?;
// SAFETY: we do not expose any other operations allowing elements of self.0 to be moved
Some(unsafe { Pin::new_unchecked(element) })
}
}
#[test]
fn demo() {
use std::task;
let mut s = PinBoxSlice::new(vec![async { 1 }]);
// Demonstrate that it is a pinning container by polling the future we put in it.
let result = s.get_mut(0).unwrap()
.poll(&mut task::Context::from_waker(task::Waker::noop()));
assert_eq!(result, task::Poll::Ready(1));
}
I suspect that it happens because you are trying to treat the whole language like an LLM would: look on the words, ignore things that are behind these words… this approach works surprisingly well in Rust till your touch unsafe… but with Pin… unsafe is more-or-less unavoidable. That's why you have trouble.
Because Pin couldn't negate fundamental rule of Rust: every type must be ready for it to be blindly memcopied to somewhere else in memory.
To ensure safety Pin have to prevent access to movable content. Otherwise compiler may happily move things behind your back.
But, of course, to make things accessible these things need to be, well… accessed, somehow. And to permit some accesses yet reject some other accesses Pin have to be in place where the type in the Pin<…> is still movable. Because that type would be moved — in these functions that provide access.
Case to the point:
How would that type ensure that you couldn't simply call reserve and wreak havoc with the vector contents? It's safe operation, it's supposed to be possible to call it on any Vec, including hyphotetical Vec<Pin<T>>…
The "unmovable pointer" conversation is probably a dead-end, but click to expand if you'd like.
Are your objects pointers? If not, that doesn't answer why you might want an "unmovable pointer".
An unmovable pointer to moveable data would be something like a Ptr<Data> such that Pin<Box<Ptr<Data>>>[1] doesn't yield a &mut Ptr<Data> (so the pointer is unmovable) but does yield a &mut Data (so it is movable), somehow.
It is a "pointer type" in the senses that it has indirection and implements Deref and DerefMut. Like the section you linked discusses, it would be useful if it had get_pin_mut or the like[2] (but couldn't allow other operations). It doesn't, because it's unclear if the std collections should or not.
So Vecspecifically hasn't taken a stance on structural pinning yet and thus isn't useful to you on its own, which is basically what some other replies talk about. But it could, and some wish it would.
Pin is a wrapper around some kind of pointer Ptr which makes that pointer “pin” its pointee value in place, thus preventing the value referenced by that pointer from being moved or otherwise invalidated at that place in memory unless it implements Unpin.
But it's understandable that one would think a Ptr<Pin<T>> is how you pin a T in place from the spelling alone.
Maybe it would be less confusing if it was spelled Pinning<Ptr<_>>.
In an alternative reality, Rust would have unmovable types instead of Pin<_>, and this part of the type system would probably be a lot less obtuse. But stable Rust had the ability to always move owned values and to move values from places you have a &mut _ to before a sufficient motivation for Pin<_> came about.[3]Pin<_> is a way to shoehorn the inability to move something into the language in a backwards compatible way, by using indirection and restricting the ability to get &mut _s sometimes.
You're correct that you don't need to involve Pin at all. Pin is there to make working with pinned things a little more safe and ergonomic.
Pin also doesn't really allow you to write self referential types, so you would need unsafe anyway. Furthermore you shouldn't need to use Pin to express any facts about your type to human readers, since the self referential nature of your type should ideally be an implementation detail.