error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/bin/live.rs:160:9
|
158 | let data = self.recv();
| --------- first mutable borrow occurs here
160 | self.on_recv(data);
| ^^^^ ----- first borrow later used here
| |
| second mutable borrow occurs here
One workaround it to pass a copy of data to on_recv, something I'd like to avoid. Another is to replace the call to on_recv with the contents of the function. Is there another approach that I might like better?
I assume that recv() would be populating a buffer owned by self. In that case recv() does not need to return the slice, instead it could return:
nothing,
a usize representing the starting index of the appended data,
a Range of the received data.
The on_recv function would also need altering, since there is no way to call &mut selfand pass it a reference to data owned by self (which if self contains a buffer, is what would be happening).
Do you mind sharing your data structure that these functions are being implemented on?
I'm using pnet::datalink for speaking Ethernet. It provides an rx/tx pair, which is where the requirement for mut comes from, and it returns the &[u8] on a receive.
If it's practical, you could split the fields of Self between two types, of which one has recv and the other on_recv. Then it becomes as simple as
fn recv_data(&mut self) {
let Self { receiver, doer } = self; // split the borrow
doer.on_recv(receiver.recv());
}
If the "doer" and the "receiver" overlap such that you can't split up Self like this, then manually inlining on_recv might be your best bet, or see this post by nikomatsakis for more ideas.
Looks useful, but not in this case. In my code self is just a wrapper around the tx and rx. The data behind the slice is buried somewhere in the pnet library.
That's what I ended up doing. One struct holds rx and implements recv(); the other holds tx and implements on_recv(). The refactoring wasn't as big a deal as I thought it would be, but I don't think the code is as easy to follow as the original. Hey, but at least it works without copying the data.
Yeah, it does copy the data.
In my experience, copying byte slices into temporary buffers is quick, and since the buffer is reused, the allocation cost is amortised.
It should be benchmarked though to see if copying the data is the bottleneck.
There might be other considerations as well, such as the recv function mutating more than just the receiver field (for example, updating a log string or counter).
I may have pointed you in the wrong direction with Vec::extend. There is a specialised function Vec in std::vec - Rust which would be more performant. Still copying though...