Arkham
Overview
Resources & State

Dependency Injection

Being able to access your own objects and state is important for any application. Arkham focuses heavily on making this ergonomic and easy.

There are two types of injectable objects a State object and Resource object. The main difference is Resource objects are provided immutable and State objects are provided with the ability to borrow both as mutable and immutable references.

Defining an injectable object

Resources and state are added during application startup and are global to the application. A single state object can be used to maintain the full state of the application and individual components can read and write to the sections they need.

Resources and state must have unique Type. Only one instance any type can be inserted.

pub struct Person {
    pub name: String,
    pub age: u16
}

#[derive(Default)]
pub struct AppState {
    pub counter: usize,
}

let people: Vec<Person> = load_people();

App::new(root_view)
    .insert_resource(people)
    .insert_state(AppState::default())
    .run();

Using resources and state

Injectables are provided automatically to any component that accepts them. Accepting them requires the use of a wrapper component depending on which it is.

  • Resources use Res<T>

  • State objects use State<T>

Using a resource

Including the resource in the function arguments automatically provides the object inside a Res wrapper, which derefs to the underlying object.

fn my_component(ctx: &mut ViewContext, people: Res<People>) {
    for (idx, person) in people.iter().enumerate() {
        ctx.insert((0, idx), person.name);
    }
}
Using state

Including the resource in the function arguments automatically provides the object inside a State wrapper. The state wrapper has two primary functions State::get which returns immutable access to the state and State::get_mut which returns a mutable reference.

fn my_component(ctx: &mut ViewContext, state: State<AppState>) {
    ctx.insert(
        (0, 0), 
        format!("Counter: {}", state.get().counter));
}

Don't borrow state mutably more than once

Under the hood state is provided inside Rc<RefCell<T>>. Take care not to call State::get_mut, which is effectively calling RefCell::borrow_mut more than once at a time.

This includes holding it and then calling a sub component that attempts to access state again. Scope calls to State::get_mut so they live as short as possible and clone out objects if needed.

Generated by Codex