We're trying to do this:
#![feature(generic_associated_types)]
use std::cell::Cell;
use std::cell::RefCell;
use std::marker::PhantomPinned;
use std::pin::Pin;
/// A self-referential struct.
pub trait SelfRef<'a> {
/// An optional "drop" function.
///
/// This is "equivalent" to `Drop` but nothing prevents calling it
/// directly so don't rely on it.
unsafe fn drop(self: Pin<&'a Self>) {
}
}
/// An opaqueified self-referential struct "key".
pub trait Opaque {
type Kind<'a>: SelfRef<'a>;
}
pub struct Holder<T: Opaque> {
inner: T::Kind<'static>,
_pinned: PhantomPinned,
}
impl<T: Opaque> Holder<T> {
pub fn new_with(f: impl for<'a> FnOnce(&'a ()) -> T::Kind<'a>) -> Self {
fn _ub_detect<T: Opaque>(
f: impl for<'x> Fn(&'x ()) -> T::Kind<'x>,
g: impl for<'x> Fn(&'x T::Kind<'x>),
) {
let _arg = ();
let _foo: T::Kind<'_> = f(&_arg);
g(&_foo);
}
/// Converts T::Kind<'a> to T::Kind<'static>
unsafe fn make_fake_static<T: Opaque>(x: T::Kind<'_>) -> T::Kind<'static> {
std::mem::transmute(x)
}
Self {
inner: unsafe { make_fake_static::<T>(f(&())) },
_pinned: PhantomPinned
}
}
pub fn operate_in<F, R>(pinned_self: Pin<&Self>, f: F) -> R
where F: for<'a> FnOnce(Pin<&'a T::Kind<'a>>) -> R {
/// Converts Pin<&'a T::Kind<'static>> to Pin<&'b T::Kind<'b>>
unsafe fn downcast_static<'a, 'b, T: Opaque>(x: Pin<&'a T::Kind<'static>>) -> Pin<&'b T::Kind<'b>> {
std::mem::transmute(x)
}
f(unsafe {
downcast_static::<T>(pinned_self.map_unchecked(|self_ref| {
&self_ref.inner
}))
})
}
}
impl<T: Opaque> Drop for Holder<T> {
fn drop(&mut self) {
// assume it was pinned.
Self::operate_in(unsafe { Pin::new_unchecked(&*self) }, |self_ref| {
unsafe { SelfRef::drop(self_ref) }
});
}
}
struct Foo<'a> {
x: Cell<Vec<&'a Foo<'a>>>,
y: RefCell<usize>,
}
struct FooOpaque {
}
impl<'a> SelfRef<'a> for Foo<'a> {
unsafe fn drop(self: Pin<&'a Self>) {
let bar = &self.x;
let inner = bar.replace(vec![self.get_ref()])[0];
println!("{:p}", self.get_ref());
println!("{:p}", inner);
println!("{}", self.y.replace(2));
}
}
impl Opaque for FooOpaque {
type Kind<'a> = Foo<'a>;
}
fn basic_tests() {
let foo = Box::pin(Holder::<FooOpaque>::new_with(|_| Foo {
x: Cell::new(Vec::new()),
y: RefCell::new(0),
}));
Holder::operate_in(foo.as_ref(), |foo| {
let bar = &foo.x;
bar.set(vec![foo.get_ref()]);
println!("{}", foo.y.replace(1));
});
let bar = foo;
Holder::operate_in(bar.as_ref(), |foo| {
let bar = &foo.x;
let inner = bar.replace(vec![foo.get_ref()])[0];
println!("{:p}", foo.get_ref());
println!("{:p}", inner);
println!("{}", foo.y.replace(2));
});
}
fn main() {
basic_tests();
}
But we can't get it to work:
error[E0512]: cannot transmute between types of different sizes, or dependently-sized types
--> src/main.rs:40:13
|
40 | std::mem::transmute(x)
| ^^^^^^^^^^^^^^^^^^^
|
= note: `<T as Opaque>::Kind` does not have a fixed size
error[E0597]: `_foo` does not live long enough
--> src/main.rs:36:15
|
36 | g(&_foo);
| ^^^^^ borrowed value does not live long enough
37 | }
| -
| |
| `_foo` dropped here while still borrowed
| borrow might be used here, when `_foo` is dropped and runs the destructor for type `<T as Opaque>::Kind<'_>`
error: aborting due to 2 previous errors
Is there any way to make it work?