macro_rules! hook_fn {
(
type Bounds = impl $hook_bound:lifetime $(+ $hook_bounds:lifetime)* ;
$($rest:tt)*
) => { ... };
($($rest:tt)*) => { ... };
}
Expand description
Write hook fn without any proc-macro.
A hook fn is a fn
which returns impl UpdateHookUninitialized
.
(Thus the output type is also UpdateHook
+ IntoHook
).
Tips: cargo fmt
supports formatting code inside hook_fn!(...);
and hook_fn![...];
, but not hook_fn!{...}
.
§Usage
hook_fn!(
fn use_constant() -> i32 {
1
}
);
/// `use_constant()` actually returns:
UpdateHookUninitialized![i32]
/// The above macro expands to
impl UpdateHookUninitialized<
Uninitialized = impl HookPollNextUpdate + HookUnmount + Default,
Hook = impl Hook + for<'hook> HookValue<'hook, Value = i32>
>
§Use other hooks with h!(...)
Usually, you would want to use other hooks in hook_fn.
use hooks::prelude::*;
hook_fn!(
fn use_auto_increment(max: i32) -> i32 {
let state = h!(use_shared_signal(0));
let v = state.get();
h!(use_effect_with::<i32, _>(|old_dependency| {
if *old_dependency != Some(v) {
*old_dependency = Some(v);
let state = state.clone();
Some(move |v: &_| state.set(*v + 1))
} else {
None
}
}));
v
}
);
§Capture lifetimes
Lifetimes in fn
generics and type Bounds = impl 'a
are captured.
For example, in
hook_fn!(
fn use_hook<'a>(_: &'a str) {}
);
the return type of use_hook
auto captures
lifetime 'a
with trait bound Captures<&'a ()>
(even the argument &'a str
is not used in the fn body).
If the lifetime is elided and used,
it must be specified with type Bounds = impl '_;
.
type Bounds = impl '_;
.hook_fn!(
fn use_hook(v: &str) {
println!("{}", v);
}
);
hook_fn!(
type Bounds = impl '_;
fn use_hook(v: &str) {
println!("{}", v);
}
);
If the lifetime comes from outer Self
and is used,
it must be specified with type Bounds = impl 'a;
.
Self
and is used,
it must be specified with type Bounds = impl 'a;
.struct Data<'a>(&'a str);
impl<'a> Data<'a> {
hook_fn!(
fn use_data(self) {
println!("{}", self.0)
}
);
}
struct Data<'a>(&'a str);
impl<'a> Data<'a> {
hook_fn!(
type Bounds = impl 'a;
fn use_data(self) {
println!("{}", self.0)
}
);
}
In Rust 2024 and later editions, all lifetimes should be auto captured
so you don’t need to specify them explicitly.
§Use an explicit identifier for hook
h!(use_hook())
actually expands to use_hook().h(hook_id)
,
where hook_id
is of type Pin<&mut UpdateHookUninitialized::Uninitialized>
.
You can use an explicit hook_id
for debugging.
hook_fn!(
fn use_my_state() -> i32 {
println!("{state_hook:?}"); // inspect this hook before it is used
let mut the_state_hook = state_hook;
let state_hook = std::pin::Pin::as_mut(&mut the_state_hook);
let (state, updater) = h![state_hook = use_shared_set(1)];
let state = *state;
println!("{the_state_hook:?}"); // inspect this hook after it is used
state
}
);
§Limitations
Only top level token trees are parsed.
The following example doesn’t work because
two macros are in a token tree ()
, which stops them to be parsed.
hook_fn!(
fn use_shared_signal_2() -> (i32, i32) {
(
h![use_shared_signal(0)].get(),
h![use_shared_signal(1)].get(),
)
}
);
You have to make sure h!
to be top level token trees when using hook_fn!
.
hook_fn!(
fn use_shared_signal_2() -> (i32, i32) {
let a = h![use_shared_signal(0)].get();
let b = h![use_shared_signal(1)].get();
(a, b)
}
);
#[hook]
parses any top level expressions (expressions that are not wrapped in {}
).
Thus, the following example works.
#[hook]
fn use_shared_signal_2() -> (i32, i32) {
(
use_shared_signal(0).get(),
use_shared_signal(1).get(),
)
}
Supports at most 10 hooks.
hook_fn!(
fn use_10_hooks() {
h!(use_hook(0)); h!(use_hook(1)); h!(use_hook(2)); h!(use_hook(3)); h!(use_hook(4));
h!(use_hook(5)); h!(use_hook(6)); h!(use_hook(7)); h!(use_hook(8)); h!(use_hook(9));
}
);
hook_fn!(
fn use_11_hooks() {
h!(use_hook(0)); h!(use_hook(1)); h!(use_hook(2)); h!(use_hook(3)); h!(use_hook(4));
h!(use_hook(5)); h!(use_hook(6)); h!(use_hook(7)); h!(use_hook(8)); h!(use_hook(9));
h!(use_hook(10));
}
);
This limitation also applies to #[hook]
because
traits are only implemented for HookTuple<(0, 1, ...)>
with at most 10 elements.
#[hook]
fn use_11_hooks() {
use_hook(0); use_hook(1); use_hook(2); use_hook(3); use_hook(4);
use_hook(5); use_hook(6); use_hook(7); use_hook(8); use_hook(9);
use_hook(10);
}
Output type must not be opaque type (impl Trait)
hook_fn!(
fn use_future() -> impl Future<Output = ()> {
async {}
}
);
#[hook]
allows to do so.
#[hook]
fn use_future() -> impl Future<Output = ()> {
async {}
}