One-level composition using reference types

Not necessary, but I'm thinking of a Node which consists of one-level internal variants like Modal, Button and more. I thought it'd be cool if someone could type button: Arc<Button> instead of button: Node and access Button as Node when needed:

// `Node` holds an internal Arc<NonRefNode>,
// so it is a "reference" type.

// what I'd like
let button: Arc<Button> = Button::new();
let button = button.base(); // button: Node

// I think I'll end up with
let button: Node  = Button::new();

Playground:, where C1 is the abstract type and C2 is the actual kind:

use std::sync::{Arc, Weak};

struct C1(Arc<C2>);

struct C2(Weak<C1>);

impl C2 {
    fn new() -> Arc<Self> {
        let mut c2 = Arc::new(C2(Weak::new()));
        let c1 = Arc::new(C1(Arc::clone(&c2)));
        Arc::get_mut(&mut c2).unwrap().0 = Arc::downgrade(&c1);
        c2
    }

    fn base(&self) -> Arc<C1> {
        self.0.upgrade().unwrap()
    }
}

fn main() {
    let c2 = C2::new();
    c2.base(); // got panic; expected no panic
}

I think this isn't entirely necessary because within my framework I'm planning to support user reactive UI components which wrap your graphical nodes using internal composition, even though nodes might not be used just for UI, but also for gaming (for graphical representation of things, together with ECS).

I'll also have these methods regardless:

  • node.to::<K>() (returns a Arc<K> by asserting the kind)
  • node.is::<K>()

For helping on node building, I could support a callback that receives the Arc<Button> for instance and use chaining methods:

let button: Node = Button::new(|btn| btn.set_warning(true).set_something_else(v));

This will still return a Node from Button::new, but allows not specifying .to::<K>:

let button: Node = Button::new();
button.to::<Button>().set_warning(true).set_something_else(v);

Maybe use trait objects?

use std::sync::Arc;

struct NodeFields {
    // Fields common to all nodes
}

trait Node {
    fn node_fields(&self) -> &NodeFields;

    fn as_button(self: Arc<Self>) -> Option<Arc<Button>> {
        None
    }
}

struct Button {
    node_fields: NodeFields,
}

impl Button {
    fn new() -> Arc<Button> {
        Arc::new(Button {
            node_fields: NodeFields {},
        })
    }
}

impl Node for Button {
    fn node_fields(&self) -> &NodeFields {
        &self.node_fields
    }

    fn as_button(self: Arc<Self>) -> Option<Arc<Button>> {
        Some(self)
    }
}

fn main() {
    let button: Arc<Button> = Button::new();
    let button: Arc<dyn Node> = button;
}

1 Like

Yeah, however node_fields() causes a dynamic dispatch for each type identifier if you're working with Arc<dyn Node>. Not necessarily "inefficient" though...