3

This answer asserts that an equivalent of C++ shared_ptr in rust is std::rc::Rc. This is true on the surface but there is an important operation missing: Specifically, shared_ptr can be used to point to an unrelated, unmanaged pointer, via its aliasing constructor (8), which is mostly used to point to a subpart of the original allocation. The shared_ptr still keeps the object alive, but the original type is completely erased. This can be very useful to forget generic parameters (and other tricks).

Obviously, in Rust, managing a completely unrelated pointer would be incredibly unsafe. Yet there is precedence of such an operation: Ref::map allows one to obtain a Ref to a component of the original Ref, while "forgetting" the type of the former.

Implementation-wise, it is clear that the current Rc<T> can not possibly implement this behaviour. When the refcount hits 0, it has to deallocate after all, and the layout must be exactly the same as when it allocated, thus it must use the same T. But that's just because it doesn't store the Layout that was originally used for the allocation.

So my question: Is there a library or other overlooked type that supports the allocation management of Rc, while also allowing the equivalent of Ref::map? I should mention that the type should also support unsize coercion.

struct Foo<T> {
    zet: T,
    bar: u16,
}

let foo: MagicRc<Foo<usize>> = MagicRc::new(Foo { zet: 6969, bar: 42 });
// Now I want to erase the generic parameter. Imagine a queue of
// these MagicRc<u16> that point to the `bar` field of different
// Foo<T> for arbitrary T. It is very important that Foo<..> does
// not appear in this type.
let foobar: MagicRc<u16> = MagicRc::map(&foo, |f| &f.bar);
// I can drop foo, but foobar should still be kept alive
drop(foo);
assert_eq!(*foobar, 42);
// unsizing to a trait object should work
let foodbg: MagicRc<dyn Debug> = foobar;

Addressing comments:

  • OwningRef and the playground link (DerefFn) do not erase the owner type.
  • Addressing Cerberus concern, such a type would store the Layout (a perfectly normal type) of the original allocation somewhere as part of the managed object and use it to free the value without having to have access to the original Type of the allocation.
WorldSEnder
  • 4,875
  • 2
  • 28
  • 64
  • There's [owning_ref](https://crates.io/crates/owning_ref), but it has some unresolved soundness issues and appears to be unmaintained. – eggyal Jan 29 '22 at 11:10
  • [reffers](https://crates.io/crates/reffers) looks like an actively maintained alternative. – eggyal Jan 29 '22 at 11:17
  • "Is there a library or other overlooked type that supports the allocation management of Rc, while also allowing the equivalent of Ref::map?" - This is probably impossible, unless you're okay with leaking the original value. The reason is simple: who would deallocate the original value, once you've done the `map`ping? (And if it is still here, well, the "managed" value would be waiting for double-free). `Ref` allows this precisely because it's not responsible for deallocation. – Cerberus Jan 29 '22 at 11:36
  • Maybe something like this quick and dirty [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d2b8740e9963c06df6922c40d6dfa88c)? – rodrigo Jan 29 '22 at 13:53
  • 2
    btw, for those who didn't know (I didn't), the OP is talking about the [aliasing constructor](https://codesynthesis.com/~boris/blog/2012/04/25/shared-ptr-aliasing-constructor/) of `shared_ptr`. The article I linked is a decent primer on it, and what it can achieve, and why you might even want to. – Kevin Anderson Jan 29 '22 at 14:07
  • Re "*`OwningRef` ... do not erase the owner type*"—see [`IntoErased::into_erased`](http://kimundi.github.io/owning-ref-rs/owning_ref/trait.IntoErased.html#impl-IntoErased%3C%27a%3E-for-Rc%3CT%3E). – eggyal Jan 29 '22 at 22:06
  • @eggyal that erases the type completely, I just want to erase the *owner type* but keep the type that I map to (the type of the `bar` field). – WorldSEnder Jan 29 '22 at 22:13
  • So then [`erase_owner`](http://kimundi.github.io/owning-ref-rs/owning_ref/struct.OwningRef.html#method.erase_owner)? – eggyal Jan 29 '22 at 22:16
  • @eggyal Indeed `OwningRef, u16>` looks like a decent solution for what I want to achieve. Want to post that as an answer? There seem to be a few unsoundness issues the library being unmaintained but that's not a deal break per se. – WorldSEnder Jan 29 '22 at 22:48
  • 1
    To be honest, I don't feel entirely comfortable posting *an answer* that recommends an unmaintained library with soundness issues. Hopefully, by leaving this unanswered, a more sound solution is more likely to emerge. – eggyal Jan 31 '22 at 06:34

0 Answers0