Idiomatic callbacks in Rust - callback

In C/C++ I'd normally do callbacks with a plain function pointer, maybe passing a void* userdata parameter too. Something like this:
typedef void (*Callback)();
class Processor
{
public:
void setCallback(Callback c)
{
mCallback = c;
}
void processEvents()
{
//...
mCallback();
}
private:
Callback mCallback;
};
What is the idiomatic way of doing this in Rust? Specifically, what types should my setCallback() function take, and what type should mCallback be? Should it take an Fn? Maybe FnMut? Do I save it Boxed? An example would be amazing.

Short answer: For maximum flexibility, you can store the callback as a boxed FnMut object, with the callback setter generic on callback type. The code for this is shown in the last example in the answer. For a more detailed explanation, read on.
"Function pointers": callbacks as fn
The closest equivalent of the C++ code in the question would be declaring callback as a fn type. fn encapsulates functions defined by the fn keyword, much like C++'s function pointers:
type Callback = fn();
struct Processor {
callback: Callback,
}
impl Processor {
fn set_callback(&mut self, c: Callback) {
self.callback = c;
}
fn process_events(&self) {
(self.callback)();
}
}
fn simple_callback() {
println!("hello world!");
}
fn main() {
let p = Processor {
callback: simple_callback,
};
p.process_events(); // hello world!
}
This code could be extended to include an Option<Box<Any>> to hold the "user data" associated with the function. Even so, it would not be idiomatic Rust. The Rust way to associate data with a function is to capture it in an anonymous closure, just like in modern C++. Since closures are not fn, set_callback will need to accept other kinds of function objects.
Callbacks as generic function objects
In both Rust and C++ closures with the same call signature come in different sizes to accommodate the different values they might capture. Additionally, each closure definition generates a unique anonymous type for the closure's value. Due to these constraints, the struct cannot name the type of its callback field, nor can it use an alias.
One way to embed a closure in the struct field without referring to a concrete type is by making the struct generic. The struct will automatically adapt its size and the type of callback for the concrete function or closure you pass to it:
struct Processor<CB>
where
CB: FnMut(),
{
callback: CB,
}
impl<CB> Processor<CB>
where
CB: FnMut(),
{
fn set_callback(&mut self, c: CB) {
self.callback = c;
}
fn process_events(&mut self) {
(self.callback)();
}
}
fn main() {
let s = "world!".to_string();
let callback = || println!("hello {}", s);
let mut p = Processor { callback };
p.process_events();
}
As before, set_callback() will accept functions defined with fn, but this one will also accept closures as || println!("hello world!"), as well as closures that capture values, such as || println!("{}", somevar). Because of this the processor doesn't need userdata to accompany the callback; the closure provided by the caller of set_callback will automatically capture the data it needs from its environment and have it available when invoked.
But what's the deal with the FnMut, why not just Fn? Since closures hold captured values, Rust's usual mutation rules must apply when calling the closure. Depending on what the closures do with the values they hold, they are grouped in three families, each marked with a trait:
Fn are closures that only read data, and may be safely called multiple times, possibly from multiple threads. Both above closures are Fn.
FnMut are closures that modify data, e.g. by writing to a captured mut variable. They may also be called multiple times, but not in parallel. (Calling a FnMut closure from multiple threads would lead to a data race, so it can only be done with the protection of a mutex.) The closure object must be declared mutable by the caller.
FnOnce are closures that consume some of the data they capture, e.g. by passing a captured value to a function that takes it by value. As the name implies, these may be called only once, and the caller must own them.
Somewhat counter-intuitively, when specifying a trait bound for the type of an object that accepts a closure, FnOnce is actually the most permissive one. Declaring that a generic callback type must satisfy the FnOnce trait means that it will accept literally any closure. But that comes with a price: it means the holder is only allowed to call it once. Since process_events() may opt to invoke the callback multiple times, and as the method itself may be called more than once, the next most permissive bound is FnMut. Note that we had to mark process_events as mutating self.
Non-generic callbacks: function trait objects
Even though the generic implementation of the callback is extremely efficient, it has serious interface limitations. It requires each Processor instance to be parameterized with a concrete callback type, which means that a single Processor can only deal with a single callback type. Given that each closure has a distinct type, the generic Processor cannot handle proc.set_callback(|| println!("hello")) followed by proc.set_callback(|| println!("world")). Extending the struct to support two callbacks fields would require the whole struct to be parameterized to two types, which would quickly become unwieldy as the number of callbacks grows. Adding more type parameters wouldn't work if the number of callbacks needed to be dynamic, e.g. to implement an add_callback function that maintains a vector of different callbacks.
To remove the type parameter, we can take advantage of trait objects, the feature of Rust that allows automatic creation of dynamic interfaces based on traits. This is sometimes referred to as type erasure and is a popular technique in C++[1][2], not to be confused with Java and FP languages' somewhat different use of the term. Readers familiar with C++ will recognize the distinction between a closure that implements Fn and an Fn trait object as equivalent to the distinction between general function objects and std::function values in C++.
A trait object is created by borrowing an object with the & operator and casting or coercing it to a reference to the specific trait. In this case, since Processor needs to own the callback object, we cannot use borrowing, but must store the callback in a heap-allocated Box<dyn Trait> (the Rust equivalent of std::unique_ptr), which is functionally equivalent to a trait object.
If Processor stores Box<dyn FnMut()>, it no longer needs to be generic, but the set_callback method now accepts a generic c via an impl Trait argument. As such, it can accept any kind of callable, including closures with state, and properly box it before storing it in the Processor. The generic argument to set_callback doesn't limit what kind of callback the processor accepts, as the type of the accepted callback is decoupled from the type stored in the Processor struct.
struct Processor {
callback: Box<dyn FnMut()>,
}
impl Processor {
fn set_callback(&mut self, c: impl FnMut() + 'static) {
self.callback = Box::new(c);
}
fn process_events(&mut self) {
(self.callback)();
}
}
fn simple_callback() {
println!("hello");
}
fn main() {
let mut p = Processor {
callback: Box::new(simple_callback),
};
p.process_events();
let s = "world!".to_string();
let callback2 = move || println!("hello {}", s);
p.set_callback(callback2);
p.process_events();
}
Lifetime of references inside boxed closures
The 'static lifetime bound on the type of the c argument accepted by set_callback is a simple way to convince the compiler that references contained in c, which might be a closure that refers to its environment, only refer to global values and will therefore remain valid throughout the use of the callback. But the static bound is also very heavy-handed: while it accepts closures that own objects just fine (which we've ensured above by making the closure move), it rejects closures that refer to local environment, even when they only refer to values that outlive the processor and would in fact be safe.
As we only need the callbacks alive as long as the processor is alive, we should try to tie their lifetime to that of the processor, which is a less strict bound than 'static. But if we just remove the 'static lifetime bound from set_callback, it no longer compiles. This is because set_callback creates a new box and assigns it to the callback field defined as Box<dyn FnMut()>. Since the definition doesn't specify a lifetime for the boxed trait object, 'static is implied, and the assignment would effectively widen the lifetime (from an unnamed arbitrary lifetime of the callback to 'static), which is disallowed. The fix is to provide an explicit lifetime for the processor and tie that lifetime to both the references in the box and the references in the callback received by set_callback:
struct Processor<'a> {
callback: Box<dyn FnMut() + 'a>,
}
impl<'a> Processor<'a> {
fn set_callback(&mut self, c: impl FnMut() + 'a) {
self.callback = Box::new(c);
}
// ...
}
With these lifetimes being made explicit, it is no longer necessary to use 'static. The closure can now refer to the local s object, i.e. no longer has to be move, provided that the definition of s is placed before the definition of p to ensure that the string outlives the processor.

If you are willing to deal with lifetime and cannot afford the heap allocation, then here is an implementation that uses reference to implement the callback:
use core::ffi::c_void;
use core::mem::transmute;
use core::ptr::null_mut;
use core::marker::PhantomData;
/// ErasedFnPointer can either points to a free function or associated one that
/// `&mut self`
struct ErasedFnPointer<'a, T, Ret> {
struct_pointer: *mut c_void,
fp: *const (),
// The `phantom_*` field is used so that the compiler won't complain about
// unused generic parameter.
phantom_sp: PhantomData<&'a ()>,
phantom_fp: PhantomData<fn(T) -> Ret>,
}
impl<'a, T, Ret> Copy for ErasedFnPointer<'a, T, Ret> {}
impl<'a, T, Ret> Clone for ErasedFnPointer<'a, T, Ret> {
fn clone(&self) -> Self {
*self
}
}
impl<'a, T, Ret> ErasedFnPointer<'a, T, Ret> {
pub fn from_associated<S>(struct_pointer: &'a mut S, fp: fn(&mut S, T) -> Ret)
-> ErasedFnPointer<'a, T, Ret>
{
ErasedFnPointer {
struct_pointer: struct_pointer as *mut _ as *mut c_void,
fp: fp as *const (),
phantom_sp: PhantomData,
phantom_fp: PhantomData,
}
}
pub fn from_free(fp: fn(T) -> Ret) -> ErasedFnPointer<'static, T, Ret> {
ErasedFnPointer {
struct_pointer: null_mut(),
fp: fp as *const (),
phantom_sp: PhantomData,
phantom_fp: PhantomData,
}
}
pub fn call(&self, param: T) -> Ret {
if self.struct_pointer.is_null() {
let fp = unsafe { transmute::<_, fn(T) -> Ret>(self.fp) };
fp(param)
} else {
let fp = unsafe { transmute::<_, fn(*mut c_void, T) -> Ret>(self.fp) };
fp(self.struct_pointer, param)
}
}
}
fn main() {
let erased_ptr = ErasedFnPointer::from_free(|x| {
println!("Hello, {}", x);
x
});
erased_ptr.call(2333);
println!("size_of_val(erased_ptr) = {}", core::mem::size_of_val(&erased_ptr));
ErasedFnPointer::from_associated(
&mut Test { x: 1},
Test::f
).call(1);
let mut x = None;
ErasedFnPointer::from_associated(&mut x, |x, param| {
*x = Some(param);
println!("{:#?}", x);
}).call(1);
}
struct Test {
x: i32
}
impl Test {
fn f(&mut self, y: i32) -> i32 {
let z = self.x + y;
println!("Hello from Test, {}", z);
z
}
}

For the type of scenario where a callback is used, you should consider the Promise alternative.
It is much easier to use than callbacks because it avoids nesting (callback hell).
Consider this:
fn main() {
let fut = do_async(&Calculation{ value: 12 });
let resp = fut().unwrap(); // call fut() to wait for the respbnse
println!("{}", resp);
}
For any calculation:
you define an struct whose fields are its inputs (name is not important).
you implement the Runner trait:
you choose what to return
you write the code of run(), which will be exected by a separate thread
struct Calculation { // <---- choose: name
value: i32 // <----- choose: inputs for your async work
}
impl Runner for Calculation {
type ReturnType = i32; // <--- choose: calculation return type
fn run(&self) -> Option<Self::ReturnType> { // <-- implement: code executed by a thread
println!("async calculation starts");
thread::sleep(Duration::from_millis(3000));
return Some(self.value * 2);
}
}
Finaly, this is the "magic":
trait Runner: Send + Sync {
type ReturnType: Send; // associated type
fn run(&self) -> Option<Self::ReturnType>;
}
fn do_async<TIn: Runner>(f: &'static TIn) -> impl FnOnce()-> Option<TIn::ReturnType> {
let (sender, receiver) = channel::<Option<TIn::ReturnType>>();
let hand = thread::spawn(move || {
sender.send(f.run()).unwrap();
});
let f = move || -> Option<TIn::ReturnType> {
let res = receiver.recv().unwrap();
hand.join().unwrap();
return res;
};
return f;
}

A simpler version of https://stackoverflow.com/a/70943671/286335 just for closures.
fn main() {
let n = 2;
let fut = do_async(move || {
thread::sleep(Duration::from_millis(3000));
return n * 1234;
});
let resp = fut(); // call fut() to wait for the response
println!("{}", resp);
} // ()
where do_async is
fn do_async<TOut, TFun>(foo: TFun) -> (impl FnOnce() -> TOut)
where
TOut: Send + Sync + 'static,
TFun: FnOnce() -> TOut + Send + Sync + 'static,
{
let (sender, receiver) = channel::<TOut>();
let hand = thread::spawn(move || {
sender.send(foo()).unwrap();
});
let f = move || -> TOut {
let res = receiver.recv().unwrap();
hand.join().unwrap();
return res;
};
return f;
} // ()

Related

What is the difference between "lazy var funcName: () -> Void" and regular function declaration in Swift

I have recently encountered such code block when I was working on a different codebase.
I was wondering is there any significant difference (in terms of memory impact etc.) between these two declarations other than syntactic ease? I regularly use lazy stored properties but I couldn't visualize how a function can be "lazy". Can you guys also enlighten me how functions are processed (or share an article explaining this topic) by the swift compiler as well?
Thanks in advance, wish you bug free codes.
A few differences I can think of off the top of my head:
With functions, you can use parameter labels to make the call site more readable, but since you can't add parameter labels to function types, you can't do that with the lazy var declaration.
// doesn't work
lazy var say: (message: String, to person: String) -> Void = {
...
}
// works
func say(message: String, to person: String) {
...
}
You can only invoke a function type with no parameter labels at all :( say("Hello", "Sweeper") instead of say(message: "Hello", to: "Sweeper").
lazy vars are variables, so someone could just change them:
helloFunc = { /* something else */ }
you could prevent this by adding a private setter, but that still allows setting it from within the same class. With a function, its implementation can never be changed at runtime.
lazy vars cannot be generic. Functions can.
// doesn't work
lazy var someGenericThing<T>: (T) -> Void = {
...
}
// works
func someGenericThing<T>(x: T) {
...
}
You might be able to work around this by making the enclosing type generic, but that changes the semantics of the entire type. With functions, you don't need to do that at all.
If you're implementing a language, the magic you need to implement 'lazy' as a feature is to make the value provided silently be wrapped in a function (of no arguments) that is automatically evaluated the first time the property is read.
So for example if you have
var x = SomeObject() // eagerly construct an instance of SomeObject
and
lazy var x = SomeObject() // construct an instance if/when someone reads x
behind the scenes you have, for the lazy var x, either x has not yet been read, or it has been and therefore has a value. So this is like
enum Lazy<T> {
case notReadYet(() -> T) // either you have a function that makes the initial T
case read(T) // or you have a value for T
}
var thing: Lazy<SomeObject> = .notReadYet { return SomeObject() }
and with a suitable property wrapper or other language magic, wrap the calls to the getter for thing with a switch on the case, which checks if you are in the notReadYet case, and if so automatically invoke the function to produce the T and then set the property to .read(t) for the particular value of t.
If you substitute in your type: () -> Void from your example you have something like:
var thing: Lazy<() -> Void> = .notReadYet({ return { print("Hello") } })
This is all rather odd because it's a void value and not much point making it lazy, e.g. it's not expensive to compute; it doesn't help break a two-phase initialisation loop, so why do it?

generic function with binding generic variable in Swift

can anyone explain what exactly does below generic class "Active" works with this - bind variable ? Below are 3 examples mentioned also but I'm really not clear how does it work
class Active<T> {
var bind :(T) -> () = { _ in }
var value :T {
didSet {
bind(value)
}
}
init(_ v :T) {
value = v
}
}
Example :
var user = Active("")
var count = Active(64)
var status = Active(true)
Exact example shown in below link
https://levelup.gitconnected.com/2-ways-to-execute-mvvm-ios-5c47d60ebcd0
If you're familiar with completion handlers, then this will make perfect sense to you. Consider a function with a barebones completion handler:
func someFunction(completion: () -> Void) {
completion()
}
someFunction {
print("completion")
}
The completion handler has no parameters and so it is called with completion() and the closure's capture list someFunction { ... } is empty. However, if we were to add a parameter to this completion handler, then it would be called with the parameter completion(true) and the closure would have to define its capture list someFunction { (done) in ... }.
func someFunction(completion: (_ done: Bool) -> Void) {
completion(true)
}
someFunction { (done) in
print(done)
}
And if we didn't care about the boolean in the completion handler then we could ignore it in the closure's capture list:
someFunction { _ in
print("completion")
}
And this is what bind is, a closure with a single parameter with a capture list that doesn't care about the parameter it was passed.
var bind: (T) -> Void = { _ in }
So the idea is to instantiate Active, with a value, and then give bind a closure to execute whenever the value changes. And so when it changes, you have bound some task to the changing of that value.
// instantiate
var count = Active(64)
// bind its change to a closure
count.bind = { _ in
// perhaps update the UI?
}
// later when the value changes, the closure is called and
// whatever task you had in the closure is executed
count.value = 128
And, as a side note, T is just an arbitrary letter (it could be 🌮) used as a placeholder for whatever type will actually be used (i.e. Int, Bool) when this object is instantiated. Whatever T is, it has to be the same type throughout the use of this object.
You give examples where you instantiate Active with different types of values (illustrating the generic behavior). But you are not availing yourself of the “observer” pattern that Active affords.
Let’s see how you would use Active:
let foo = Active(0)
foo.bind = { value in
print(value)
}
foo.value = 1 // the closure will be called, printing the value
foo.value = 42 // the closure will be called again
The idea is that you instantiate the object, replace the bind closure with your own, and then the closure will be called every time you change the value.
The virtue of this “observer” pattern is that Active is providing a mechanism (a simple closure in this primitive example) to allow other objects to observe changes to the value. This is one of the central precepts of MVVM (as that article you quote is attempting to convey), namely, that you can write code that, for example, automatically updates a view based upon changes to a model.

How does one define optional methods on traits?

Does Rust have a feature whereby I can create define potentially non-existent methods on traits?
I realize that Option can be used to handle potentially non-existent properties but I don't know how the same can be achieved with methods.
In TypeScript, the question mark denotes that the methods may potentially be non-existent. Here is an excerpt from RxJs:
export interface NextObserver<T> {
next?: (value: T) => void;
// ...
}
If this feature does not exist in Rust, how should one think about dealing with objects whereby the programmer doesn't know whether a method will be present or not? Panic?
You can try using empty default implementations of methods for this:
trait T {
fn required_method(&self);
// This default implementation does nothing
fn optional_method(&self) {}
}
struct A;
impl T for A {
fn required_method(&self) {
println!("A::required_method");
}
}
struct B;
impl T for B {
fn required_method(&self) {
println!("B::required_method");
}
// overriding T::optional_method with something useful for B
fn optional_method(&self) {
println!("B::optional_method");
}
}
fn main() {
let a = A;
a.required_method();
a.optional_method(); // does nothing
let b = B;
b.required_method();
b.optional_method();
}
Playground

Chained generic type inferred closures in swift

I am struggling to conceptualise in my head how to create a generic interface or class which functions
like so.
functionOne {
return "123"
}
.chainedFunction { string -> Int in
return Int(string) + 456
}
.anotherChain {
[NSNumber($0), String($0)]
}
.lastInTheChain {
print("value 1: \($0[0])")
print("value 2: \($0[1])")
}
so for this to work it is essentially an array of functions that pipe its return value into the
next function's parameter list.
assuming each function closure was named after the function it came from
i.e
functionOne(_ functionOne:(T) -> T) {}
chainedFunction(_ chainedFunction:(T) -> T) {}
then you would then call them like so to make sure you pass the correct values through.
lastInTheChain(anotherChain(chainedFunction(functionOne())))
The part i struggly mostly with is the Generics part and whether type inference will work or not
(this is a secondary concern, would just be nice if it worked)
Im also attempting to use protocols, and generic protocols in swift require the use of thunks here and there which is only compounding my confusion.
So anyone out there a tad more knowledgeable if you would not mind chipping in how i could do this or whether it is doable in the first place i would be most appreciative.
functionOne needs to return something that has a chainedFunction instance method, which itself needs to return something that has an anotherChain instance method, and so on. For this, you need to create a type. I'll call it wrapper because I don't know your use case, but you should name it something more meaningful.
import Foundation
struct Wrapper<T> {
let value: T
// This replaces `function1`
init(_ value: T) { self.value = value }
func chainedFunction<R>(_ transform: (T) -> R) -> Wrapper<R> {
return Wrapper<R>(transform(self.value))
}
func anotherChain<R>(_ transform: (T) -> R) -> Wrapper<R> {
return Wrapper<R>(transform(self.value))
}
func lastInTheChain<R>(_ transform: (T) -> R) -> R {
return transform(self.value)
}
}
Wrapper("123")
.chainedFunction { string -> Int in
return Int(string)! + 456
}
.anotherChain {
[NSNumber(value: $0), String($0)]
}
.lastInTheChain {
print("value 1: \($0[0])")
print("value 2: \($0[1])")
}
Terminology: this Wrapper type is called a functor, because it defines a map method (here called chainedFunction and anotherChain, which are equivalent functions in the example). Using map, a closure which can be used to transform T to R (transform) can be used to transform a Wrapper<T> into a Wrapper<R>.

How to do `Hash` to a unit struct in rust?

I'd like to implement a kind of thing which can be uniquely identified, besides, it contains no other fields. It 's kinds of like BasicObject in ruby, or Object in java.
I have added a PartialEq trait.
struct Thing;
impl PartialEq for Thing {
fn eq(&self, other: &Thing) -> bool {
unsafe {
self as *const Thing == other as *const Thing
}
}
}
Now, I want to allow that it can be hashed, and the question is how could I implement Hash trait for a unit struct which has no fileds ? Or, should I add some kind of object_id to it ?
You cannot rely on pointer address to determine for sure unicity of unit structs in Rust, for example the following code will print two times the same address:
struct Foo;
fn main() {
let x = Foo;
let y = Foo;
println!("{}", &x as *const _);
println!("{}", &y as *const _);
}
You need to have them occupy at least one byte in memory, for example by giving them a field that you never use :
struct Thing {
x: u8;
}
impl Thing {
fn new() -> Thing {
Thing { x: 0u8 }
}
}
Then, a simple way would be to continue on the same track as you are going, and using the address of your objects in memory as a base value for the Hash :
use std::hash::{hash,Hash};
use std::hash::sip::SipState;
struct Thing;
impl Hash for Thing {
fn hash(&self, state: &mut SipState) {
let addr = self as *const Thing as u64;
addr.hash(state);
}
}
fn main() {
let f = Thing;
println!("{}", hash(&f));
}
Note : casting &Thing to *const Thing (and then to u64) is not unsafe, so you don't need the unsafe{} block in your implementation of PartialEq.