Comments (7)
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.
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.
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.
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.
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.
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.
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)
- Real-world benchmarks
- Link the docs website from somewhere HOT 1
- Feature request: elapsed and/or simulation start time HOT 3
- Timer queue and time/duration overhaul
- `stakker_macros` linked from website doesn't exist HOT 3
- Macro to support `Actor<Box<dyn Trait>>` based actor creation HOT 1
- Can not initialise actor using fully qualified init method HOT 2
- Allow returning `Self` instead of `Option<Self>` for Prep methods that always go to Ready
- Means to make actor calls from another thread HOT 1
- Clarify timer contract
- Glommio: Consider writing interface code to run on top of it
- More beginner friendly tutorial? HOT 6
- ActorOwnAnon type HOT 1
- Pass long path name to macro HOT 2
- ret_some_do! updating variables issue HOT 7
- Look into a no_std variant of stakker
- Actor coroutines
- Consider small-Ret optimisation to avoid allocation
- Are there any benchmarks? HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from stakker.