[Resolved] Entities/structures/trait objects with >= 1 pointers?

I would like to replicate this type in C:

struct IFoo *FooTable[256];

Right now, I have something like this:

struct MyThing<'a> {
    foo_table: [Option<Rc<&'a dyn FooTrait>>; 256],
}

However, I'm getting just a ton of errors when attempting to manipulate this field. Too many to list here, which indicates that after years of working with Rust, I still don't understand how to make an entity that can have several pointers to it.

What am I doing wrong? To me the above type makes perfect sense:

  • Optional<>, because it's potentially NULLable.
  • Rc<>, because it's reference counted.
  • &'a dyn FooTrait because it's a Trait Object (yes, by intention).

I don't understand what else I could possibly need here? Any elucidation would be very helpful, thanks!

EDIT: I forgot to mention that I've also been searching the web for help on this and trying my best to RTFM, but to no avail. Despite there being a ton of sites that cover things like Rc vs Arc, etc, I'm still not able to find a site that clearly illustrates the simple use-case of having a structure with n pointers to it from different places/scopes.

A struct containing a reference is only useful in very limited ways, and I assume this is the cause of most/some of the errors you're seeing.

What you may be missing is that Rc is already a pointer, so you don't need another reference inside the Rc. I suggest trying:

struct MyThing {
    foo_table: [Option<Rc<dyn FooTrait>>; 256],
}

This more directly maps to your C struct.

You may run into additional problems because the Option is the outermost wrapper, but one step at a time.

3 Likes

I don't think you ever want Rc<&T> as a field.

If you can make a data structure work with Rc<&T>, it's almost certain you could make it work with &T. Shared references (&T) implement Copy and have no-op destructors; the Rc<_> doesn't add anything useful.[1] You could have many independent Rc<&T>'s pointing to the same place.


@jumpnbrownweasel's suggestion is probably what you want. The only thing I'll add is that it's possible you'll get borrow checker errors if your FooTrait implementors are not 'static, in which case you could attempt one of these:

struct MyThing<'a> {
    foo_table: [Option<Rc<dyn FooTrait + 'a>>; 256],
}

// N.b. `&'a dyn FooTrait` is shorthand for `&'a (dyn FooTrait + 'a)`
struct MyThing<'a> {
    foo_table: [Option<&'a dyn FooTrait>; 256],
}

If none of these suggestions work, you may be dealing with general borrow checker errors (that we don't have enough context to comment on).


  1. They do not keep the T alive, just as &T on its own does not, in case you thought they would. â†Šī¸Ž

2 Likes

There isn't a day that goes by when Rust doesn't somehow teach me something new. (Or, at least, something that feels new to me.)

I don't normally need to deal with these kinds of types because the default collections are just too convenient. Alas, in this context, I'm writing code that is intended to resemble no_std/alloc-less code, so I'm trying to dig a little deeper than what I normally do. Eventually, I'm hoping I can more easily port this code to an STM32 microcontroller.

I also completely forgot about RefCell as well, which is something else I'll be needing. :wink:

Thanks for the replies; I'll try to apply your collective wisdom as soon as my lunch break is over.

It looks like the final type that ended up working for my needs is:

Option<Rc<RefCell<dyn FooTrait>>>

That is a real mouthful; and, simply accessing the field involves more match statements than I'd prefer; but, it finally works, and I'm able to move on with my project.

Many thanks to everyone who helped!

1 Like