Callback calling a type method

I want to create a method to return a callback that calls another method (for abstraction)
I made sample code, obviously it throws lifetime error but i want to know if it is possible in rust and how can it be done

cannot infer an appropriate lifetime due to conflicting requirements src\clipboard_holder.rs:46:27

	use std::thread;
use crossbeam::channel::{unbounded, Sender};
use std::sync::{Arc};

pub type ClipboardSenderType = Sender<String>;

pub type CallbackType = Arc<Box<dyn Fn(&String) + Send + Sync>>;

pub struct ClipboardHolder {
	history: Vec<String>,
	pub history_sender: ClipboardSenderType
}
impl ClipboardHolder {
	pub fn new() -> Self {
		let (history_sender, history_receiver) = unbounded();
		// Managing clipboard history and state
		thread::spawn(move || {
			loop {
				println!("{:#?}", history_receiver.recv().unwrap());
			}
		});

		ClipboardHolder {
			history: Vec::new(),
			history_sender
		}
	}

	pub fn safe_history_add(&mut self, content: Box<&String>) {
		self.history_sender.send(content.to_string());
	}

	pub fn history_add(&mut self) {

	}

	pub fn history_get_last_remove(&mut self) {

	}

	pub fn history_get_total(&mut self) {

	}

	pub fn sender_callback_create(&mut self) -> CallbackType {
		Arc::new(Box::new(move | text: &String | { self.safe_history_add(Box::new(text)); }))
	}
}

Okay, let’s see, first off:

yes, it ought to be possible. Let’s look at your code.

Just looking at this type signature reveals that CallbackType cannot contain a reference to self, since it does not have any lifetime parameters that would allow it to contain such a reference. The reference is needed so that the callback can modify/access the original self object. In case this is not clear to you, maybe the desugaring might help. The signature would look like this:

pub fn sender_callback_create<'a>(&'a mut self) -> CallbackType

What we need to do to resolve this is add a lifetime parameter to CallbackType and use it to constrain the lifetime of the closure inside it like this:

pub type CallbackType<'a> = Arc<Box<dyn Fn(&String) + Send + Sync + 'a>>;

Then the callback-creating function can look like this:

pub fn sender_callback_create<'a>(&'a mut self) -> CallbackType<'a>

Which can be written more concisely as

pub fn sender_callback_create(&mut self) -> CallbackType<'_>

Second problem: You will not be able to access your ClipboardHolder anymore after calling sender_callback_create until the closure is dropped (or more specifically until it isn’t used anymore). The reason being that you’re borrowing self mutably here and the lifetime 'a of that borrow is for as long as the resulting CallbackType<'a> lives. (For closures containing mutable references there are further things to consider regarding Fn vs FnMut and how Arc is not allowing mutable access, but the next paragraph solves those aswell.)

You had the correct intuition on how to allow for it to be used while the callback still exists, by using some synchronization mechanism (Sender) inside safe_history_add. One important detail about the send method in Sender that you might have missed is that it’s only needing &self (where self is referring to the Sender) without mut. So let’s now solve the problem described above and change safe_history_add and sender_callback_create into not using mutable references to self anymore, and you’ll get a program that compiles. (playground)

3 Likes

BTW, you don't need double indirection of Arc<Box>, you can use Arc<dyn Fn>.

3 Likes

Thanks for the detailed explanation.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.