-
-
Notifications
You must be signed in to change notification settings - Fork 719
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
use_callback should take a closure that accepts an argument #2371
Labels
hooks
Changes to built-in hook package
Comments
since I needed this I've hacked something together (mostly copy-paste of dioxus code, plus one argument) that appears to work for me. Let me know if this looks plausible and if so I'll create a PR! pub fn use_callback1<T, O>(f: impl FnMut(T) -> O + 'static) -> UseCallback1<O, T> {
// Create a copyvalue with no contents
// This copyvalue is generic over F so that it can be sized properly
let mut inner = use_hook(|| CopyValue::new(None));
// Every time this hook is called replace the inner callback with the new callback
inner.set(Some(f));
// And then wrap that callback in a boxed callback so we're blind to the size of the actual callback
use_hook(|| {
let cur_scope = current_scope_id().unwrap();
let rt = Runtime::current().unwrap();
UseCallback1 {
inner: CopyValue::new(Box::new(move |arg1| {
// run this callback in the context of the scope it was created in.
let run_callback = || inner.with_mut(|f: &mut Option<_>| f.as_mut().unwrap()(arg1));
rt.on_scope(cur_scope, run_callback)
})),
}
})
}
/// This callback is not generic over a return type so you can hold a bunch of callbacks at once
///
/// If you need a callback that returns a value, you can simply wrap the closure you pass in that sets a value in its scope
#[derive(PartialEq)]
pub struct UseCallback1<O: 'static + ?Sized, T: 'static> {
inner: CopyValue<Box<dyn FnMut(T) -> O>>,
}
impl<O: 'static + ?Sized, T> Clone for UseCallback1<O, T> {
fn clone(&self) -> Self {
Self { inner: self.inner }
}
}
impl<O: 'static, T: 'static> Copy for UseCallback1<O, T> {}
impl<O, T> UseCallback1<O, T> {
/// Call the callback
pub fn call(&self, arg1: T) -> O {
(self.inner.write_unchecked())(arg1)
}
}
// This makes UseCallback callable like a normal function
impl<O, T> std::ops::Deref for UseCallback1<O, T> {
type Target = dyn Fn(T) -> O;
fn deref(&self) -> &Self::Target {
// https://github.com/dtolnay/case-studies/tree/master/callable-types
// First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>).
let uninit_callable = MaybeUninit::<Self>::uninit();
// Then move that value into the closure. We assume that the closure now has a in memory layout of Self.
let uninit_closure = move |arg1| Self::call(unsafe { &*uninit_callable.as_ptr() }, arg1);
// Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure.
let size_of_closure = std::mem::size_of_val(&uninit_closure);
assert_eq!(size_of_closure, std::mem::size_of::<Self>());
// Then cast the lifetime of the closure to the lifetime of &self.
fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
b
}
let reference_to_closure = cast_lifetime(
{
// The real closure that we will never use.
&uninit_closure
},
// We transmute self into a reference to the closure. This is safe because we know that the closure has the same memory layout as Self so &Closure == &Self.
unsafe { std::mem::transmute(self) },
);
// Cast the closure to a trait object.
reference_to_closure as &_
}
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Feature Request
Currently there's no way to pass in an argument to the closure that use_callback wraps. This should be easy enough to add. I think the only reason we didn't add it is to make the type signature of UseCallback a bit more compact, but that's not so important.
The text was updated successfully, but these errors were encountered: