Code Monkey home page Code Monkey logo

Comments (7)

veniamin-ilmer avatar veniamin-ilmer commented on May 22, 2024

Providing concrete example of what I am talking about:

Here I define the parent's trait inside of the child.

So this way, the child only references the parent for all io requests. All chips would do this, so they don't communicate with each other. This way you can mix and match any chip to communicate with any other chip.

The glue between all the chips would be handled by one single parent, implementing all of these chips.

Here is an example board implementing the chip
Here is the same board implementing a different chip

But actor of trait seems to only allow for one trait
So I can pass it to here but not to here.

Does it make sense why I am interested in allowing for multiple traits for one actor?

from stakker.

uazu avatar uazu commented on May 22, 2024

You can define a trait made up of other traits:

trait A: B + C {}

Then you could use actor_of_trait! with that trait.

However I suspect that this does not solve your problem, at least not immediately. I guess you want each chip to receive a reference to the board actor that lists only the traits that that chip requires, not all possible traits. So this means casting one trait to another. This may be possible, but I have not tried it. For example you need to cast Actor<Box<dyn ABCDTrait>> to Actor<Box<dyn BDTrait>> (where ABCDTrait is ATrait+BTrait+CTrait+DTrait, and BDTrait is BTrait + DTrait).

I wonder whether Rust is able to do these kinds of casts. It is from one dynamic trait to another, but still everything is static knowledge, at least locally to where the actor is created. It may be possible. If you look at the actor_of_trait! macro, there is no magic there. All the magic is done by Rust itself. So writing out the actor_of_trait! macro manually may allow you to try to adjust things to find a way. This is a general Rust question so maybe someone has found a way in another context.

from stakker.

uazu avatar uazu commented on May 22, 2024

I tried the casting without success. The process of going from Box<MyType> to Box<dyn MyTrait> is called coercion. It's some magic that Rust does when it detects that it is required. It involves adding a vtable pointer outside the box, i.e. making it a fat pointer. However Actor<Box<MyType>> is similar to Rc<RefCell<Box<MyType>>>>, so the casting doesn't work, because it means adding a vtable pointer within an already-allocated cell. So right now I can't see any way to cast between different traits. (However maybe there is a way, but I can't see it right now.)

But certainly you can do the multiple-traits thing, by defining a single trait which includes all of them.

from stakker.

veniamin-ilmer avatar veniamin-ilmer commented on May 22, 2024

I was able to define:

trait AllTraits: Intel4004IOTrait + Intel4002IOTrait {}
type AllBox = Box<dyn AllTraits>;

let board = actor_of_trait!(stakker, AllBox, MC4::init(), ret_nop!());

However, I am not sure what to do from here. I understand Rust loses the ability to cast the trait once it is wrapped in something like a RefCell.

Reading online it seems like RefCell, and hence Actor can't cast due to invariance.
RefCell<T> is invariant over T.
https://rust-lang.github.io/rfcs/0738-variance.html
https://www.reddit.com/r/rust/comments/v9a9dq/comment/ibx8z8i/

Is there a way to briefly unwrap the Actor to cast the trait? Or somehow don't wrap the trait into an Actor until later?

Alternatively

Here is a proof of concept of how to can cast it with generics: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=21fa200c0aea421fea892e7334db616a

But I am not sure how to convert the test2<T: ATrait + ?Sized>(var: Rc<RefCell<Box<T>>>) into this:

pub type BoxedTrait = Box<dyn ATrait>;
pub trait ATrait {
  fn example_function(&self, cx: CX![BoxedTrait]);
}

Because it is no longer Box<dyn ..> anymore..

from stakker.

uazu avatar uazu commented on May 22, 2024

I don't think it can be done with this approach. If you do Actor<Box<dyn Trait>>, the vtable is stored within stakker-internal structures. If somehow you could change it there, it would change for everyone referencing the actor. (Incidentally, Stakker doesn't use Rc<RefCell<...>>, but what it does use does the same job, so I mentioned that to make it easier to understand.)

However if you look at this alternate method of doing dyn Trait, that uses a wrapper. You could have several wrappers for different traits. Effectively Rust will create a different wrapper for each combination of external trait and board (since it is generic). The efficiency should be about the same, but the interface looks different. Although it doesn't look quite so clear in the code (actor calls look like normal calls), I wonder whether that could help.

To be honest in this kind of situation I've used the third method on that page, i.e. using Fwd. So I've not got to the point of having to solve these tricky trait problems.

It would be nice if we could find a clean way of doing this, but this faces Rust limitations. Maybe if one day I could implement Actor<dyn Trait> directly, then maybe the vtable pointer would be outside and that would solve the problem. But I don't think everything I need is implemented yet in Rust, especially unsized enums. I suppose I could have a go using ManuallyDrop, but it will require unsafe and will make a whole load of simple code more complicated. I may have a quick look to see if Rust will let me do this now, but previously the lack of unsized enums was the problem.

from stakker.

veniamin-ilmer avatar veniamin-ilmer commented on May 22, 2024

Omg, that was a lot of indirection / boilerplate... But it works! Thank you!

I don't understand if I would be able to use Fwd! here.. It seems like it requires an ActorOwnAnon object, however this is a Child referring a parent, so the Child would need an actor object, not an actor own object..

from stakker.

uazu avatar uazu commented on May 22, 2024

You don't need to have an ActorOwnAnon at all. That is only if you don't have anything else owning the actor, to keep it from being killed off. A Fwd is like an Actor reference combined with a bit of code to forward a call to that actor. So for creating interconnections between actors, it is ideal. One actor can pass the Fwd onto another and the actor doesn't need to know which other actor it is sending calls to at all. It is like a stream or channel, but there is no "close". Perhaps better see it more like an interconnect or a patch-cable. So you can definitely do what you need with Fwd, but maybe it is not convenient because you might need a lot of them if you traits have lots of methods (i.e. you'd need one for each trait method). But one advantage is that the calls don't need to go via the motherboard. They could go directly from one chip to another.

Sorry about the boilerplate. I was looking again at Actor<dyn Trait>, which is what you really want. Still I hit the same problem with Rust not allowing an unsized enum. However, if I use my unsized_enum crate, I think I could implement it. Perhaps I should do that, with an optional crate feature to enable it. But my unsized_enum crate has not been reviewed for soundness, so it's not really suitable for people who want maximum safety.

from stakker.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.