1

I'm trying to provide "views" of non-owned structs to separate components of a system.

Assume a set of traits with distinct methods: Drawable, Modifiable and a number of structs which implement at least one of the traits - SimpleBox, Panel, Expression.

Different components of the system will need to frequently access sequences of these objects, using methods of specific traits; consider a DrawingManager or a ModifyManager:

struct DrawingManager {
    items: Vec<Weak<Drawable>>,
}

struct ModifyManager {
    items: Vec<Weak<Modifiable>>
}

While a single object may be referenced in both managers, assume that there is a separate single owner of all structs:

struct ObjectManager {
    boxes: Vec<Rc<Box>>,
    panels: Vec<Rc<Panel>>,
    expressions: Vec<Rc<Expression>>,
}

Ideally, it would be useful to be able to manage deleting structs from one place - i.e simply removing it from the ObjectManager being enough to invalidate references in all other components (hence the use of Weak).

  • Is there a way of doing this?
  • Is this the correct way to achieve this?
  • Is there a more idiomatic way of implementing this functionality?

The system contains several traits, so making a single trait using methods of all the other traits seems like a bad idea. Several traits have more than one method, so replacing them with closures is not possible.

What I have tried

As one object may produce one or more Rc<Trait>, we might envision implementing this with a HashMap<ID, Vec<Rc<Any>>> whereby we make each struct have a unique ID, which maps to a list of all Rc that have been made for it.

When we want to remove an object, we remove it from the corresponding list, and remove the entry in the hashmap, invalidating all Weak references.

However, implementing this fails, as to insert into the HashMap, one must upcast a Rc<Trait> -> Rc<Any>, only to downcast it later.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • *Is there a way of doing this?* and *Is this the correct way to achieve this?* are somewhat at odds. Does the solution you have do what you want, or not? If not, what is wrong about it? – trent Aug 01 '18 at 00:10
  • Sorry my wording was unclear. I don't have a compiling solution. – Kiran Gopinathan Aug 01 '18 at 04:30
  • While you can cast a convert a `Rc -> Rc` by cloning, and then convert a reference to that to `Weak` using `Rc::downgrade`, I have not found a suitable way to first store the `Rc` into a sequence of a supertype (for example `Vec>`, and then downcast the reference to the supertype back into a reference to the original type (`&Rc -> &Rc`) . – Kiran Gopinathan Aug 01 '18 at 04:55
  • I've answered at least [two](https://stackoverflow.com/questions/27892375/can-i-do-type-introspection-with-trait-objects-and-then-downcast-it) [questions](https://stackoverflow.com/questions/40024093/downcast-traits-inside-rc-for-ast-manipulation?noredirect=1&lq=1) about downcasting in Rust. I'm thinking of another one (not written by me) but I can't find it right now. Does one of those answer your question? – trent Aug 01 '18 at 12:32
  • The main gist of this question isn't about down-casting - rather obtaining Weak references - this provides two important features - maintaining a single "owner" of data, while also allowing treating multiple references the same - i.e holding a sequence of `vec>`, rather than several lists of each independent type. It just so happens that in my example I have an issue with downcasting (but only because I upcasted immediately prior). – Kiran Gopinathan Aug 01 '18 at 18:57
  • The use of downcast ref doesn't help, as the implementation I have tried requires converting a `&Rc -> &Rc`, (unlike `&Rc -> Option<&Trait>`, which `downcast_ref` provides). It seems the only way to do this is to use `std::mem::transmute()`, which doesn't seem very idiomatic. – Kiran Gopinathan Aug 01 '18 at 19:00

1 Answers1

1

I'm not sure if this is the idiomatic way of doing this, but I've since developed a crate providing this functionality - dependent_view.

Using the crate, the initial problem can be solved by using DependentRc instead of plain Rc's:

struct ObjectManager {
    boxes: Vec<DependentRc<Box>>,
    panels: Vec<DependentRc<Panel>>,
    expressions: Vec<DependentRc<Expression>>
}

let object_manager : ObjectManager = ObjectManager::new();

Then using macros provided by the crate, we can obtain Weak<> references to these structs:

let box_view : Weak<Drawable> = to_view!(object_manager.boxes[0]);
let panel_view : Weak<Drawable> = to_view!(object_manager.panels[0]);
let expression_view : Weak<Drawable> = to_view!(object_manager.expressions[0]);

With this, dropping the corresponding DependentRc<> will invalidate all Weak<> references that have been made of it.