This hasn't got much to do with Pin at all. You are trying to create a self-referential type, which is not possible in safe Rust.
Don't try to have references to the data in the same instance. Re-design your data structure so ownership and the borrowing parts are clearly separated.
Yeah I figured, I just didn't know what to title it and wasn't confident enough to cite it as self-reference issue. My post history says it all: self references in rust, bane of my existence.
It might be worthwhile describing the thought process that leads to a design with self-referential types in Rust; while I used self-referential types quite a bit in C and C++, I've not come up with a design that needs them in the last 6 years of using Rust (I did in my early days, and hit all the issues that you're hitting).
I (and probably other contributors) would be happy to talk through a design where you think you need self-referential types, and help you design something that doesn't need them - hopefully helping you also understand how you can do the design yourself in future.
FWIW, you could force your way into such a pattern by using unsafe code; in which case you could take advantage of the guarantees Pin can offer. But then it is paramount to think of what the resulting exposed public API is.
So we could start off the following (which still suffers from the same lifetime problem, but at least it tries to get the Pinning semantics right):
Mainly, we try to make it more explicit that the reference inside the now named SliceRef struct is a pinned one, which thus could be related to self-referentiability or whatnot.
We also make the field private anyways, so as to avoid leaking that 'a-bounded reference, since with self-references, there is no proper lifetime parameter in Rust to use (any choice of 'a will effectively be a lie, w.r.t. the short-lived-ness of '_ in self: Pin<&'_ mut Self> of poll), so we have to fall back to privacy, since we effectively we have 'a = 'unchecked, sort to speak (in fact, it might even be wise to be using ptr::NonNull<T> rather than Pin<&'a T> altogether, but let's stick to this 'a-approach for the moment).
Now, what do we have:
fn poll is taking a self: Pin<&'_ mut Enum<'a>> type, which is a Pin-wrapped pointer to a non-Unpin type (thanks to our PhantomPinned). Thus, by witnessing such a thing, we know, as per the Pin guarantees, that the pointee —here, Enum<'a>—, shall henceforth not be moved, deällocated, or more generally, invalidated, before calling its drop glue (said drop glue is not guaranteed to run either, in which case the pointee would remain valid to access 'forever = 'static).
This means that, for good measure, we could slap an impl Drop on our type to make sure we clear such self-reference:
#[::pin_project::pin_project(
PinnedDrop,
project = EnumProject,
)]
pub
enum Enum<'a> {
A(
Option<SliceRef<'a>>,
#[pin] (Vec<u8>, PhantomPinned),
),
}
#[::pin_project::pinned_drop]
impl PinnedDrop for Enum<'_> {
fn drop(self: Pin<&'_ mut Self>)
{
match self.project() {
EnumProject::A(s, _) => {
// clear the `'a` reference just in case.
*s = None;
},
// ...
}
/* rest of drop logic (if any) */
}
}
(it is true that since we are dropping our self-reference anyways, there is technically no need to do this here. But certain patterns might have the "back-reference" lying elsewhere, and keeping this workflow in mind is the right mindset when working with Pin: too many people forget to think of the drop glue property of Pin when, in fact, it's the actual sole property which Pin offers on which everything hinges. Lack of movability (before drop) is a corollary, even if a useful one).
And now that we have covered both the pub API, and adjusted it to the Drop guarantee of Pin, we may finally actually involve unsafe code to lengthen a &'_-limited borrow (with '_ stemming from the self: Pin<&'_ mut /* impl !Unpin */> receiver) up to an "arbitrary" lifetime (one which we know won't be accessible beyond the 'drop point):
impl<'a> Stream for Enum<'a> {
type Item = ();
fn poll_next<'poll>(
self: Pin<&'poll mut Self>,
_cx: &mut std::task::Context<'_>,
) -> Poll<Option<Self::Item>>
{
match self.project() {
EnumProject::A(s, v) => {
let self_ref: Pin<&'a (Vec<u8>, PhantomPinned)> = unsafe {
pin_guarantee_lengthen::<'poll, 'a, _>(v)
}.into_ref();
*s = Some(SliceRef(self_ref.get_ref().0.as_slice()));
}
};
todo!()
}
}
unsafe
fn pin_guarantee_lengthen<'short, 'careful, T>(
r: Pin<&'short mut T>,
) -> Pin<&'careful mut T>
where
// for the sake of extra documentation,
// let's allow these lifetime params to be turbofishable.
'short :,
'careful :,
{
unsafe {
::core::mem::transmute(r)
}
}
@Yandros Thank you for offering a decent, direct solution. I'll need some time off to try this code out and understand it. Since it's unsafe I wont use it just yet. For now I am just cloning the vec it is what it is.
@farnz I generally don't design my way into needing self-referential types. It's just my least understood part of rust, and therefore it will be what I often post about. I come back to rust every year or so to try things out. I retain most of my knowledge, except for dealing with lifetimes and cycles. So I constantly need a refresher. Apparently I even understood self-referential types enough to make this post. But sometimes I encounter a novel self-referential problem that I can't square with my current understanding.
As a rule of thumb, it's a part of Rust to avoid by designing things to not need self-referential types; I used to have trouble with them, but I found that, with a bit of self-reeducation, I don't need them at all any more.
And that's what I'd like to be able to pass on to you, if possible - the change in thinking that means that you don't need to deal with self-referential types because you've been able to restructure into a non-self-referential type, or able to exploit {Arc, Rc}::new_cyclic to produce a cyclic data structure instead that's easier to work with than a self-referential one was.