Crates.io | gear-objects |
lib.rs | gear-objects |
version | 0.3.0 |
source | src |
created_at | 2024-03-02 01:15:17.697266 |
updated_at | 2024-03-08 03:09:30.704017 |
description | Rust component object model |
homepage | https://github.com/jesse99/gear |
repository | https://github.com/jesse99/gear |
max_upload_size | |
id | 1159400 |
size | 102,397 |
Gear is a component object model for rust. So why does rust need a new object model? And why gear?
Grady Booch defined four pillars of OOP:
These are all useful and work well in important domains like GUIs, sims, games, plugin architectures, etc.
impl Trait
or Box<dyn Trait>
. But again, there
are composibility problems. For example, it quickly gets very awkward if you want a
Checkbox that can act Like a Button which can act like a View.The gear object model consists of:
For example, you might have a component which exposes a Draw trait:
let component = get_from_somewhere();
let render = find_trait!(component, Render).unwrap();
render.render(&mut canvas);
The repro has an example that consists of a wolf vs sheep simulation. Here is some annotated code from that to illustrate how gear works:
// Gear currently requires nightly and you'll need this in your main.rs
// (or lib.rs).
#![feature(lazy_cell)]
#![feature(ptr_metadata)]
#![feature(unsize)]
use core::sync::atomic::Ordering;
use gear_objects::*;
use paste::paste;
// One of the traits the sim components expose. This is used by the World
// struct to render all of the components in the sim.
pub trait Render {
fn render(&self) -> ColoredString;
}
// Traits exposed by components have to be registered.
register_type!(Render);
// Function to add a wolf to the world and to the Store (the Store
// manages component lifetimes).
pub fn add_wolf(world: &mut World, store: &Store, loc: Point) -> ComponentId {
// The name "wolf" is used with Component's Debug trait.
let mut component = Component::new("wolf");
// Each component is given a unique id allowing components to be
// compared and hashed.
let id = component.id;
add_object!(
component,
Wolf, // object type
Wolf::new(), // object
[Action, Animal, Predator, Render], // traits exposed by this object to the component
[Debug] // repeated traits (these can appear multiple times in a Component)
);
// Wolf is the main object but there are a couple other objects
// which allow code reuse between Wolf and Sheep.
add_object!(component, Mover, Mover::new(), [Moveable]);
add_object!(
component,
Hungers,
Hungers::new(INITAL_HUNGER, MAX_HUNGER),
[Hunger],
[Debug] // Debug is repeated for this Component
);
// Animals are added to the back and grass to the front.
world.add_back(store, loc, component);
id
}
// Concrete object type, not directly used by client code.
struct Wolf {
age: i32, // wolves can die of old age
}
register_type!(Wolf);
// Traits are implemented normally.
impl Render for Wolf {
fn render(&self) -> ColoredString {
if self.age == 0 {
"w".green()
} else {
"w".normal()
}
}
}
// Somewhat simplified version of the World::render method.
// The World works with any components that implement the
// Action and Render traits.
pub fn render(&self, store: &Store) {
for y in 0..self.height {
for x in 0..self.width {
let loc = Point::new(x, y);
// Render the last component at a loc.
if let Some(id) = self.actors.get(&loc).map(|v| v.last()).flatten() {
let component = store.get(*id);
let render = find_trait!(component, Render).unwrap();
let ch = render.render();
print!("{}", ch);
} else {
print!(" ");
}
}
println!();
}
println!();
}