Hi. I was always wondering how Adobe AIR could fit into the actual Rust language. What do you think?
Document-object-model
There are two ways to write a document-object-model (or "display list" in Adobe AIR), usually with reference-counting:
- Composition: Composition that uses a limited set of variants inside, boxing each variant, so that the union of these variants is always the size of a native pointer.
- Traits: Dynamic dispatch through use of abstract traits that share the same fields through a
common()
method that returns these common fields and provides methods that operate over this common()
method (that is, a dynamic dispatch occurs everytime an abstract method that uses common()
is called).
Traits vs. composition:
- With the traits approach you can have implicit covariance. For example, you can take a
Modal
as a Node
without calling something like modal.base()
.
- With the traits approach you can call the abstract trait's methods on an opaque node kind. With the composition approach you've to always hold the node as
Node
somewhere (not as Button
for example), otherwise you lose it all and can't even go back to Node
.
- With the traits approach you can extend the document-object-model of the framework with your own node type at your end.
- Although with the traits approach you can extend the document-object-model with your own node type, that was never used very much in pratice in the browser, except by the Angular framework, which uses a concept called custom elements in current browsers.
- Also, even with traits you can't extend existing node types. There's only one-level inheritance.
Composition vs. traits:
- With the composition approach, all accesses to a node's common properties do not occur after a dynamic dispatch, different from a traits approach. In the traits approach, common abstract methods like
get_skin()
and also children manipulations occur after a call to common()
, which tests the actual node kind against every kind of the document-object-model.
- With the composition approach, you limit the set of node kinds so that no one can extend it.
What are traits:
- Traits are abstract types that can be implemented for opaque types. It's like Java interfaces, but with optional (provided) methods.
Here's an example structuring a node type with composition (playground), supporting concurrency (that is, the graphical node can be passed around in concurrent systems like entity-component-systems if the target platform supports multi-threading).
- All
Node
s have a parent()
method. The parent is behind a weak reference since reference counting is used inside instead of garbage collection.
Button
has a property that is specific to it, very_specific_property
, therefore Node
doesn't support that property within it, requiring a .to::<Button>()
conversion right before accessing it.
Example code:
let button = Button::new(|button|
button.set_very_specific_property(10));
assert_eq!(button.parent(), None);
assert_eq!(button.to::<Button>().get_very_specific_property(), 10);
Explanation:
std::sync::RwLock
is similiar to a mutex. It's used for mutable memory locations that are "atomic". It wraps a value.
- The
type T;
thing inside a trait is an associated type. It's like a generic parameter, but expressed separately from the angle brackets (S<T>
).
Optional chaining
There are different ways to do optional chaining, but the current stable way is using callbacks:
some_optional_value.map(|value| value.another_operation())
There's the try
expression that can be combined with the None
or Err
propagation operator (?
), but it's nightly.
Asynchronous code
The only things asynchronous in ActionScript are usually timers... and events...
The Rust language does have the core Future
interface and .await
operators for instance, but no runtime, since it's a systems language that is used for any purpose. I want to finish an utilities API that hides clutter in the sense of generic graphical applications.
Regarding timing API and futures API:
- There can be more additions to futures, based on the crates from the ecosystem, but I'm thinking of aliasing or wrapping them only if frequently used. The futures part isn't hard, but the timers one took 2 days from me as it must work in the browser too.
If you want to take a look at these insignificant things specifically:
(Not published to crates.io. The existing version there is gonna be replaced.)
Usually these things are all re-exported by rialight::prelude
for including directly in the lexical scope.
rialight::main!(async {
// asynchronous entry point
});
Explanation:
main
is a macro that expands to yet another code that creates the asynchronous runtime. For the browser, it uses simply promises. For anything other than the browser, it uses the tokio
dependency.
async {}
is an "asynchronous" block. This is part of the Rust language: it evaluates to a future. You can write an asynchronous function too and call it, but you may prefer using just async {}
.
exec_future
can be used to execute futures in the background, i.e., calling them without awaiting for their result.
rialight::util
includes several things that any generic graphical program uses, but I need this to finish it and move on to something else in Rialight:
- Temporal API from the TC39 proposal (mimmicked).
Regarding Rust conditional compilation:
- I got some confusions on whether to detect the browser target as the
wasm32
architecture or not. A Rust's target triple has a "OS" portion, not a platform portion, possibly causing wrong interpretation (because wasm
may be used to target yet other platforms). However, targetting wasm32
to compile to, say, Nintendo Switch's Nvidia chip, isn't viable because of the Rust standard library that panics for wasm32
in some functions instead of delegating to system calls, so I think it'll be useless distinguishing the browser from wasm32
(what I'm already doing).
- To distinguish the browser and
wasm32
I'm having to use two Cargo features: rialight_browser_export
and rialight_default_export
. The former indicates the browser regardless of the target architecture and the latter indicates any platform that's not the browser.