I asked a question about explicit cloning and closures a few days ago and turns out there's a way around it.
As shown in the example bellow, the reactive-graph
library of the Leptos framework managed to pass variables without the Copy
trait such as String
to closures without explicitly cloning them.
use reactive_graph::{owner::ArenaItem, signal::ArcRwSignal, traits::Read};
fn callback_mut(mut cb: impl FnMut() + 'static) { cb(); }
fn main() {
let counter_arena = ArenaItem::<_>::new_with_storage(ArcRwSignal::new("example1".to_owned()));
callback_mut(move || { println!("Arena 1: {}", counter_arena.try_get_value().unwrap().read().to_string()); });
callback_mut(move || { println!("Arena 2: {}", counter_arena.try_get_value().unwrap().read().to_string()); });
}
Repo: https://github.com/LiveDuo/rust-reactive-graph-example/blob/master/src/main.rs
In their docs, Leptos says the following:
This is the innovation that allows Leptos to be usable as a Rust UI framework. Traditionally, managing UI state in Rust has been hard, because UI is all about shared mutability. (A simple counter button is enough to see the problem: You need both immutable access to set the text node showing the counter's value, and mutable access in the click handler, and every Rust UI framework is designed around the fact that Rust is designed to prevent exactly that!) Using something like an event handler in Rust traditionally relies on primitives for communicating via shared memory with interior mutability (
Rc<RefCell<_>>
,Arc<Mutex<_>>
) or for shared memory by communicating via channels, either of which often requires explicit.clone()
ing to be moved into an event listener. This is kind of fine, but also an enormous inconvenience.Leptos has always used a form of arena allocation for signals instead. A signal itself is essentially an index into a data structure that's held elsewhere. It's a cheap-to-copy integer type that does not do reference counting on its own, so it can be copied around, moved into event listeners, etc. without explicit cloning.
While the docs kind of mention arena allocators there's no enough info to understand how it works and recreate an example locally without reactive-graph
.
Does anyone knows how to build a minimal example without using another crate? Any good reading on arena allocators?