![1c165ea69b5547da420eb132506f4658.png](https://i-blog.csdnimg.cn/blog_migrate/6facbabf4c05d767dc899ab7499772da.jpeg)
![b5afd2a40bd6dcb0e4bb4baa7059c6e0.png](https://i-blog.csdnimg.cn/blog_migrate/d0595088d52d4a68864c1f25414335bd.jpeg)
From this article I will continue writing in English. The following articles could be uncomfortable to read at first. But I will try my best to improve my English-writing skill.
The titles of the following articles remain in Chinese, because Zhihu limits English title unacceptable shortly.
The re-designed Memory
and Runtime
structs are agnostic to the concrete type of objects. Pratically the type will keep the original Box<dyn Object>
structure. This structure assures constant size known at compile time, while permits flexiable implementation of Object
by various custom types. However, I will try my best to ignore this fact when dealing with Memory
and Runtime
, because it makes me confused about the funtionality of them quite much. As a vivid example, I will introduce Method
and AsMethod
traits, which are deeply affected by this new-styled Runtime
.
The two traits Method
and AsMethod
lives besides the definition of Object
previously. However, the reason why they exist is Runtime
, who is responsible to invoking methods. To resolve the conflict above, I defined "could be executed as a method (possibly)" as one of two basic features of objects. It works well, just a little annoying when I question myself why a submodule in core
has to use a submodule not in core
.
At the same time, MethodObject
in objects
submodule also has a strong connection on the conception of Runtime
(and RuntimeError
). And Object
, desipte no direct relationship with Runtime
, relies on MethodObject
through its as_method
interface which is required to exist by AsMethod
super trait. It is more annoying that Runtime
appears in the conception of Object
, which should be as "pure" as possible in my thoughts.
After eliminating Object
trait objects from Runtime
, the very first thing I did is removing core::object
and objects
submodules in a whole. This doesn't mean I think they are not an essential part of Shattuck. They just need a full rewriting.
When I was doing this, I realized that Runtime
cannot execute any object as method any more. It knows nothing about its type arguments L
and S
, except their relationship to each other. They may implement Object
, and they may not. Actually, these is not a Object
for them to implement at that time (and currently).
The Runtime
be hardly useful without the missing function. To find a way to re-add it, I need to think of a central problem first:
Who requiresAsMethod
,Object
orRuntime
?
Suddenly I found benefit that I previously ignored. With the action of extracting object type into type arguments from Runtime
, I got a chance to design a "slimmer" version of Object
. It is not required to have any feature by default. It is just required to exist. Anyone who wants to filter out objects with specific functionality defines its own As*
trait. For Runtime
, it needs to determine the objects which could be treated as methods, so it introduces AsMethod
. It does not need to access any properties of any objects (for now), so AsProp
could be safely excluded from all the conception currently.
And what's more (which looks like an overdesign), the AsMethod
trait is only required to be implemented by objects when we need Runtime
to have ability to call method objects indeed. So the "common" methods, like share
and insert
, could live in an impl
block which doesn't bound AsMethod
to any of L
, S
and O
.
Speaking of this, AsMethod
is only required for S
after some thinking. Like my explaination in the 4th article, method objects need to be copied before executed, and it would be too limited to require L
to be Clone
able. S
is stored in Arc<RwLock<...>>
in Runtime
, which implements Clone
inherently. Thus, a method object will be share
d before executed. In this way, the only limitation to the native code inside is that it cannot borrow the method object itself mutably (aka, cannot call write
on it). This is absolutely reasonable for a method object.
The part of code looks like this:
impl<O, L, S> Runtime<L, S>
where
O: ?Sized,
S: From<L> + DerefMut<Target = O> + AsMethod<L, S>,
L: DerefMut<Target = O>,
{
pub fn call(&mut self, method: usize) -> Result<(), RuntimeError> {
let share_object = Arc::clone(&self.share(method)?.0);
let read_method = share_object
.read()
.map_err(|_| RuntimeError::AccessConflict)?;
read_method.as_method()?.run(self)
}
}
Notice the redundant part after where
. It seems there's no way to abstract them into a trait or something. What a pity.
![1c165ea69b5547da420eb132506f4658.png](https://i-blog.csdnimg.cn/blog_migrate/6facbabf4c05d767dc899ab7499772da.jpeg)