qnilab
qnilab

Reputation: 474

Closure referencing object on which it is attached

Here is the code (playground here) for a hypothetical socket that would write data back (as defined in a closure) when some data is received.

pub struct Socket {
    on_message_received: Box<dyn FnMut(&Vec<u8>, &mut Socket)>,
}

impl Socket {
    pub fn new(on_message_received: Box<dyn FnMut(&Vec<u8>, &mut Socket)>) -> Self {
        Self {
            on_message_received,
        }
    }

    pub fn read(&mut self, fake_data: &Vec<u8>) {
        (self.on_message_received)(fake_data, self);
    }

    pub fn write(&mut self, data: Vec<u8>) {
        println!(
            "Pretend this requires a mutable reference to self {:?}",
            data
        );
    }
}

fn main() {
    let mut socket = Socket::new(Box::new(|data, socket| {
        socket.write(data.clone());
    }));
    socket.read(&vec![1, 2, 3]);
}

The line (self.on_message_received)(fake_data, self); fails to compile because we cannot borrow *self as mutable more than once at a time. I understand this but cannot think of a workaround.

I've also tried (other playground) to define the closure in a setter (instead of in the constructor) and make the closure capture a reference to the socket (instead of letting the closure be invoked with a reference to the socket).

But in the end, I always face the same issue where a double borrow of mutable reference occurs. 🤔

Upvotes: 2

Views: 46

Answers (1)

cdhowie
cdhowie

Reputation: 169318

If you can get by with Fn instead of FnMut then you can solve this problem by using Arc instead of Box, and cloning the Arc before you call the closure:

use std::sync::Arc;

pub struct Socket {
    on_message_received: Arc<dyn Fn(&Vec<u8>, &mut Socket)>,
}

impl Socket {
    pub fn new(on_message_received: Arc<dyn Fn(&Vec<u8>, &mut Socket)>) -> Self {
        Self {
            on_message_received,
        }
    }

    pub fn read(&mut self, fake_data: &Vec<u8>) {
        let cb = self.on_message_received.clone();
        cb(fake_data, self);
    }

    pub fn write(&mut self, data: Vec<u8>) {
        println!(
            "Pretend this requires a mutable reference to self {:?}",
            data
        );
    }
}

fn main() {
    let mut socket = Socket::new(Arc::new(|data, socket| {
        socket.write(data.clone());
    }));
    socket.read(&vec![1, 2, 3]);
}

(Playground)

By cloning the Arc, we get a handle to the closure that is independent of the lifetime of self and therefore doesn't require a borrow of self. This frees us to reborrow self and give it to the closure.

Notably, this approach allows Socket::read to be fully re-entrant -- the closure can call socket.read() without any issues (other than the potential for infinite recursion, of course).


If you would like you can hide the Arc detail from the consumer by taking impl Fn in Socket::new and creating the Arc there:

use std::sync::Arc;

pub struct Socket {
    on_message_received: Arc<dyn Fn(&Vec<u8>, &mut Socket)>,
}

impl Socket {
    pub fn new(on_message_received: impl Fn(&Vec<u8>, &mut Socket) + 'static) -> Self {
        Self {
            on_message_received: Arc::new(on_message_received),
        }
    }

    pub fn read(&mut self, fake_data: &Vec<u8>) {
        let cb = self.on_message_received.clone();
        cb(fake_data, self);
    }

    pub fn write(&mut self, data: Vec<u8>) {
        println!(
            "Pretend this requires a mutable reference to self {:?}",
            data
        );
    }
}

fn main() {
    let mut socket = Socket::new(|data, socket| {
        socket.write(data.clone());
    });
    socket.read(&vec![1, 2, 3]);
}

Upvotes: 1

Related Questions