Callback to mutable self - rust

Is there a way (in rust) to send a mutable borrowed self to a callback without the mem::replace hack I am using in the following MWE? I am using rust stable (1.11.0).
use std::mem;
trait Actable {
fn act(&mut self);
}
// Not Cloneable
struct SelfCaller {
message: String,
callback: Box<FnMut(&mut SelfCaller)>,
// other stuff
}
impl Actable for SelfCaller {
fn act(&mut self) {
fn noop(_: &mut SelfCaller) {}
let mut callback = mem::replace(&mut self.callback, Box::new(noop));
callback(self);
mem::replace(&mut self.callback, callback);
}
}
impl Drop for SelfCaller {
fn drop(&mut self) {/* unimiportant to the story */}
}
fn main() {
fn change(messenger: &mut SelfCaller) {
messenger.message = "replaced message".to_owned();
}
let mut messenger = SelfCaller {
message: "initial message".to_owned(),
callback: Box::new(change),
};
messenger.act();
println!("{}", &messenger.message);
}
Play

No, there is no way, because it is unsafe to do so. Here's an example that demonstrates why (requires a nightly compiler).
#![feature(fn_traits)]
#![feature(unboxed_closures)]
use std::mem;
trait Actable {
fn act(&mut self);
}
struct SelfCaller {
message: String,
callback: Box<FnMut(&mut SelfCaller)>,
}
impl Actable for SelfCaller {
fn act(&mut self) {
let mut callback: &mut Box<FnMut(&mut SelfCaller)> = unsafe { mem::transmute(&mut self.callback) };
println!("calling callback");
callback(self);
println!("called callback");
}
}
struct Callback;
impl Drop for Callback {
fn drop(&mut self) {
println!("Callback dropped!");
}
}
impl<'a> FnOnce<(&'a mut SelfCaller,)> for Callback {
type Output = ();
extern "rust-call" fn call_once(mut self, args: (&mut SelfCaller,)) {
self.call_mut(args)
}
}
impl<'a> FnMut<(&'a mut SelfCaller,)> for Callback {
extern "rust-call" fn call_mut(&mut self, (messenger,): (&mut SelfCaller,)) {
println!("changing callback");
messenger.callback = Box::new(|messenger| {});
println!("changed callback");
messenger.message = "replaced message".to_owned();
}
}
fn main() {
let change = Callback;
let mut messenger = SelfCaller {
message: "initial message".to_owned(),
callback: Box::new(change),
};
messenger.act();
println!("{}", &messenger.message);
}
The output of this program is:
calling callback
changing callback
Callback dropped!
changed callback
called callback
replaced message
OK, so what's going on? First, I've written the implementation of act for SelfCaller in such a way that I can call the callback without mem::replace, using mem::transmute to get the compiler to generate a new lifetime disconnected from self.
Then, I've written a callback (using the struct Callback, since I needed a type that implements both FnMut and Drop to demonstrate the problem) that mutates the SelfCaller by changing its callback member. This has the effect of dropping the previous callback, which is the callback that is currently executing! If Callback contained data members, attempting to read them would cause undefined behavior, since they are now in deallocated memory (we dropped the whole Box).
By the way, in your code using mem::replace, callbacks cannot change the callback, since you restore the callback after the callback call ends.

No, this is not possible with your code. If it were possible, you could easily construct an example that destroys memory safety, by for example accessing freed memory (this is left as an exercise for the reader 😉).
You could think about whether or not the FnMut really needs all of the fields of SelfCaller. If not, you can pass the (hopefully few) single fields as arguments. If not, you can create another type (let's call it Inner) that contains all fields important to the callback and pass it to the function.

If you don't need callbacks that borrow an environment, you can use a function instead of the closure:
trait Actable {
fn act(&mut self);
}
struct SelfCaller {
message: String,
callback: fn(&mut SelfCaller),
}
impl Actable for SelfCaller {
fn act(&mut self) {
(self.callback)(self);
}
}
fn main() {
fn change(messenger: &mut SelfCaller) {
messenger.message = "replaced message".to_owned();
}
let mut messenger = SelfCaller {
message: "initial message".to_owned(),
callback: change,
};
messenger.act();
println!("{}", &messenger.message);
}

Related

How to resolve the problem when borrowing more than one time in Rust?

I have a structure that contains a TcpStream for communication and a BytesMut for receiving data.
When I need to use it to receive data, I intend to do the following.
#[tokio::test]
async fn test_refmut1() {
struct Ctx {
tcps: TcpStream,
data: BytesMut,
}
async fn recv(ctx: Arc<Mutex<Ctx>>) {
let mut ctx = ctx.lock().await;
ctx.tcps.read_buf(&mut ctx.data).await.unwrap();
}
}
Obviously, this can not compile, because tcps is borrowed once, and BytesMut, which is the read_buf() parameter, is borrrowed again.
As usual, I wrapped the other part using RefCell to get internal mutability.
#[tokio::test]
async fn test_refmut2() {
struct Ctx {
tcps: TcpStream,
data: RefCell<BytesMut>,
}
async fn recv(ctx: Arc<Mutex<Ctx>>) {
let mut ctx = ctx.lock().await;
let tcps = &ctx.tcps;
let mut data = ctx.data.borrow_mut();
ctx.tcps.read_buf(&data).await.unwrap();
}
}
However, this still doesn't compile because read_buf() requires an argument of type &mut BytesMut, which I now borrowed via RefCell as an argument of type RefMut<BytesMut>.
But I know that the two are not directly convertible, what should I do?
The lock() method does not provide a reference but a MutexGuard.
When using this guard multiple times, we borrow it the same number of times and this can introduce the problem you report.
One solution is to obtain the reference held by this guard only once (the &mut * trick, is actually .deref_mut()), then use this reference multiple times, relying on split-borrow as stated in other answers.
async fn test_refmut1() {
struct Ctx {
tcps: TcpStream,
data: BytesMut,
}
async fn recv(ctx: Arc<Mutex<Ctx>>) {
let mut ctx_guard = ctx.lock().await;
let ctx = &mut *ctx_guard;
ctx.tcps.read_buf(&mut ctx.data).await.unwrap();
}
}
Obviously, this can not compile, because tcps is borrowed once, and BytesMut, which is the read_buf() parameter, is borrrowed again.
That's not an issue? Rust can split borrows, though sometimes you have to nudge it a bit (might depend on the edition you're using). I whipped up a quick variant and it compiles fine:
use std::io::*;
struct Ctx {
tcps: Stdin,
data: Vec<u8>,
}
fn recv(mut ctx: Ctx) {
ctx.tcps.read_to_end(&mut ctx.data).unwrap();
}
fn main() {
let ctx = Ctx { tcps: stdin(), data: Vec::new() };
recv(ctx);
}
You can split borrow the Ctx, a clean way is to destructure a &mut version of the context:
#[tokio::test]
async fn test_refmut1() {
struct Ctx {
tcps: TcpStream,
data: BytesMut,
}
impl Ctx {
async fn read(&mut self) {
let Self { tcps, data } = self;
tcps.read(data).await.unwrap();
}
}
async fn recv(ctx: Arc<Mutex<Ctx>>) {
let mut ctx = ctx.lock().await;
ctx.read();
}
}
Playground

How to store a callback in a struct that can change the struct's internal state?

In one of my projects, I would like to store a function pointer used as a callback to change the state of a struct. I've tried different things, but always encountered errors.
Consider the following situation (playground):
struct CallbackStruct {
callback: fn(i32) -> i32,
}
struct MainStruct {
pub callback_struct: Vec<CallbackStruct>,
pub intern_state: i32,
}
impl MainStruct {
pub fn new() -> MainStruct {
let result = MainStruct {
callback_struct: Vec::new(),
intern_state: 0,
};
// push a new call back struct
result.callback_struct.push(CallbackStruct{callback: result.do_stuff});
return result;
}
pub fn do_stuff(&mut self, i: i32) -> i32 {
self.intern_state = i * 2 + 1;
self.intern_state
}
}
fn main() {
let my_struct = MainStruct::new();
}
Here, I'm trying to keep a callback to the MainStruct, that can change it's internal state. This callback will only be stored by other structs owned by this main structure, so I don't think I'm having lifetimes issues - as long as the callback exists, the main struct does as well as it kind of own it.
Now, I'm wondering if having such a callback (with the &mut self reference) isn't a borrow of the main struct, preventing me from having multiple of them, or even keeping them?
In the example, I'm keeping a Vec of the CallbackStruct because I may have different structs all having these kinds of callbacks.
In c/c++, I can go with functions pointers, and I couldn't find a way to store such things in Rust.
How would one implement such a thing?
The problem that Rust is preventing is the possibility that being able to mutate the struct that holds the callback allows you to mutate or destroy the callback while it is executing. That is a problem.
If it is a common pattern that you want your callbacks to modify the structure that invokes the callback, then there's a couple options. Both require adjusting the function signature to pass-in data they are allowed to mutate. This would also allow multiple callbacks to mutate the same state since they only hold the mutable reference while they are running.
#1: Keep the state and callbacks separate
The idea is that the callback is explicitly given mutable access to the state which does not include the callback itself. This can be done by constructing a separate structure to hold the non-callback data, as shown here, or you can simply pass in multiple parameters:
struct InternalState(i32);
impl InternalState {
fn do_stuff(&mut self, i: i32) {
self.0 = i * 2 + 1;
}
}
struct MainStruct {
callbacks: Vec<Box<dyn FnMut(&mut InternalState)>>,
state: InternalState,
}
impl MainStruct {
fn new() -> MainStruct {
MainStruct {
callbacks: Vec::new(),
state: InternalState(0),
}
}
fn push(&mut self, f: Box<dyn FnMut(&mut InternalState)>) {
self.callbacks.push(f);
}
fn invoke(&mut self) {
for callback in &mut self.callbacks {
callback(&mut self.state)
}
}
}
fn main() {
let mut my_struct = MainStruct::new();
my_struct.push(Box::new(|state| { state.do_stuff(1); }));
my_struct.push(Box::new(|state| { state.do_stuff(2); }));
my_struct.invoke();
dbg!(my_struct.state.0);
}
#2 Remove the callback from the struct before executing it
You can mutate the whole struct itself if you remove the callback being ran. This can be done via the take-and-replace method used in this question. This has the added benefit that you have the opportunity to add new callbacks; you just have to reconcile the two sets of callbacks when putting them back:
struct InternalState(i32);
impl InternalState {
fn do_stuff(&mut self, i: i32) {
self.0 = i * 2 + 1;
}
}
struct MainStruct {
callbacks: Vec<Box<dyn FnMut(&mut MainStruct)>>,
state: InternalState,
}
impl MainStruct {
fn new() -> MainStruct {
MainStruct {
callbacks: Vec::new(),
state: InternalState(0),
}
}
fn push(&mut self, f: Box<dyn FnMut(&mut MainStruct)>) {
self.callbacks.push(f);
}
fn invoke(&mut self) {
let mut callbacks = std::mem::take(&mut self.callbacks);
for callback in &mut callbacks {
callback(self)
}
self.callbacks = callbacks;
}
}
fn main() {
let mut my_struct = MainStruct::new();
my_struct.push(Box::new(|main| { main.state.do_stuff(1); }));
my_struct.push(Box::new(|main| { main.state.do_stuff(2); }));
my_struct.invoke();
dbg!(my_struct.state.0);
}
You'll also noticed I changed the code from function pointers fn(i32) -> i32 to function trait objects Box<dyn FnMut(i32) -> i32> since the latter is much more flexible and common since it can actually capture other variables if needed.

Using a trait object in a background job (different thread)

I want to have a background worker which uses a trait implementation / object for some time. The background worker owns this object as long as it is used. After the background worker is "destroyed", the object should be free to be used again.
I tried to make all the things with async/await, but it produced some more problems. Therefore, I use plain threads to create kind of a minimal example. First I also used Box<&dyn mut...> to pass the object to the background worker, but I think that is not even needed.
My minimal example contains a MyWriter-trait which can write string to somewhere. There exists one implementation which writes strings to stdout. A background-worker uses this writer for a background-job. The worker has a start-method to start the job and a stop-method to join it (in my real code I would use a channel to send a stop-info to the worker and joining then).
I'll post the code and then a description with my problems:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=a01745c15ba1088acd2e3d287d60e270
use std::sync::Arc;
use std::sync::Mutex;
use std::thread::{spawn, JoinHandle};
/* Trait + an implementation */
trait MyWriter {
fn write(&mut self, text: &str);
}
struct StdoutWriter {}
impl StdoutWriter {
pub fn new() -> Self {
Self {}
}
}
impl MyWriter for StdoutWriter {
fn write(&mut self, text: &str) {
println!("{}", text);
}
}
/* A background job which uses a "MyWriter" */
struct BackgroundJob<'a> {
writer: Arc<Mutex<&'a dyn MyWriter>>,
job: Option<JoinHandle<()>>,
}
impl<'a> BackgroundJob<'a> {
pub fn new(writer: &'a mut dyn MyWriter) -> Self {
Self {
writer: Arc::new(Mutex::new(writer)),
job: None,
}
}
pub fn start(&mut self) {
assert!(self.job.is_none());
let writer = &self.writer;
self.job = Some(std::thread::spawn(move || {
// this background job uses "writer"
let mut my_writer = writer.lock().unwrap();
my_writer.write("x");
// do something
my_writer.write("y");
}));
}
pub fn stop(&mut self) {
if let Some(job) = self.job {
job.join().unwrap();
self.job = None;
}
}
}
/* Using BackgroundJob */
fn main() {
let mut writer = StdoutWriter::new();
writer.write("a");
{
let mut job = BackgroundJob::new(&mut writer);
// inside this block, writer is owned by "job"
job.start();
job.stop();
}
// writer should be usable again
writer.write("b");
}
The desired output on stdout is a\nx\ny\nz\n, but the program does not even compile. My main problem is that (dyn MyWriter + 'a) cannot be shared between threads safely (compiler error).
How can I implement Send / Sync for a trait? It does not seem to be possible. Actually, I assumed it should be ok if the object (or a ref.) is inside Arc<Mutex<...>>, but that does not seem to be sufficient. Why not?
Maybe someone has an idea how this can be fixed or even more important what exactly is the underlying issue?
Putting a reference in an Arc doesn't work. Since the Arc can be kept alive indefinitely simply by cloning it, the reference could easily outlive whatever it was borrowed from, so that can't compile. You need to put an owned object in the Arc, such as Box<dyn MyWriter>. (Ideally you'd just use Arc<dyn MyWriter>, but that would conflict with returning the writer from the BackgroundJob, as shown below.)
Since you can't borrow from writer in main, you must move it into the BackgroundJob. But at this point you've relinquished ownership over writer, having moved the value to BackgroundJob, so your only option is to have BackgroundJob return the writer. However, since BackgroundJob keeps its writer behind a trait object, it can only give back the Box<dyn MyWriter> it stores, not the original StdoutWriter.
Here is the version that works that way, retaining type erasure and giving back the type-erased writer:
// Trait definition and StdoutWriter implementation unchanged
struct BackgroundJob {
writer: Arc<Mutex<Box<dyn MyWriter + Send>>>,
job: Option<JoinHandle<()>>,
}
impl BackgroundJob {
pub fn new(writer: Box<dyn MyWriter + Send>) -> Self {
Self {
writer: Arc::new(Mutex::new(writer)),
job: None,
}
}
pub fn start(&mut self) {
assert!(self.job.is_none());
let writer = Arc::clone(&self.writer);
self.job = Some(std::thread::spawn(move || {
// this background job uses "writer"
let mut my_writer = writer.lock().unwrap();
my_writer.write("x");
// do something
my_writer.write("y");
}));
}
pub fn stop(&mut self) {
if let Some(job) = self.job.take() {
job.join().unwrap();
}
}
pub fn into_writer(self) -> Box<dyn MyWriter> {
Arc::try_unwrap(self.writer)
.unwrap_or_else(|_| panic!())
.into_inner()
.unwrap()
}
}
fn main() {
let mut writer = StdoutWriter::new();
writer.write("a");
let mut writer = {
let mut job = BackgroundJob::new(Box::new(writer));
job.start();
job.stop();
job.into_writer()
};
writer.write("b");
}
Playground
A version that gave back the writer of the same type would have to give up on type erasure and be generic over the writer type. Though a bit more complex, its ownership semantics would be very close (at least conceptually) to what you originally envisioned:
struct BackgroundJob<W> {
writer: Arc<Mutex<W>>,
job: Option<JoinHandle<()>>,
}
impl<W: MyWriter + Send + 'static> BackgroundJob<W> {
pub fn new(writer: W) -> Self {
Self {
writer: Arc::new(Mutex::new(writer)),
job: None,
}
}
// start() and stop() are unchanged
pub fn into_writer(self) -> W {
Arc::try_unwrap(self.writer)
.unwrap_or_else(|_| panic!())
.into_inner()
.unwrap()
}
}
fn main() {
let mut writer = StdoutWriter::new();
writer.write("a");
{
// inside this block, writer is moved into "job"
let mut job = BackgroundJob::new(writer);
job.start();
job.stop();
// reclaim the writer
writer = job.into_writer();
}
writer.write("b");
}
Playground
The main issue is that you want to pass a reference to the thread. The problem with that approach is that the thread can outlive the referenced object. Obviously this does not happen in your case, but the rust compiler cannot reason about that.
The solution to that problem is to use Arc<Mutex<dyn MyType>> instead of Arc<Mutex<&dyn MyType>> - no lifetimes - no problems.
The next issue is with Mutex<T> - it can be send across threads only if T can. So you have to make T, in your case dyn MyType, implement Send. This can be done in two ways:
Make MyType require Send - in that case that trait can be implemented only by Send objects:
trait MyWriter : Send{
fn write(&mut self, text: &str);
}
Or use an additional trait bound - in that case your trait is less restrictive, but you must always specify MyTrait + Send when you want to send it across threads:
Arc<Mutex<dyn MyWriter + Send>>
So far so good, but now your new() method does not work, because dyn MyWriter is not Sized. In order to fix that you have to make your method generic:
pub fn new<T: MyWriter + Send>(writer: T) -> Self {
Self {
writer: Arc::new(Mutex::new(writer)),
job: None,
}
}
or directly pass an Arc<Mutex<dyn MyWriter + Send>>:
pub fn new(writer: Arc<Mutex<dyn MyWriter + Send>>) -> Self {
Self { writer, job: None }
}
Full working code
use std::sync::Arc;
use std::sync::Mutex;
use std::thread::JoinHandle;
trait MyWriter {
fn write(&mut self, text: &str);
}
struct StdoutWriter {}
impl StdoutWriter {
pub fn new() -> Self {
Self {}
}
}
impl MyWriter for StdoutWriter {
fn write(&mut self, text: &str) {
println!("{}", text);
}
}
/* A background job which uses a "MyWriter" */
struct BackgroundJob {
writer: Arc<Mutex<dyn MyWriter + Send>>,
job: Option<JoinHandle<()>>,
}
impl BackgroundJob {
pub fn new(writer: Arc<Mutex<dyn MyWriter + Send>>) -> Self {
Self { writer, job: None }
}
pub fn start(&mut self) {
assert!(self.job.is_none());
let writer = self.writer.clone();
self.job = Some(std::thread::spawn(move || {
let mut my_writer = writer.lock().unwrap();
my_writer.write("x");
// do something
my_writer.write("y");
}));
}
pub fn stop(&mut self) {
if let Some(job) = self.job.take() {
job.join().unwrap();
}
}
}
fn main() {
let mut writer = StdoutWriter::new();
writer.write("a");
let writer = Arc::new(Mutex::new(writer));
{
let mut job = BackgroundJob::new(writer.clone());
// inside this block, writer is owned by "job"
job.start();
job.stop();
}
// you have to acquire the lock in order to use the writer
writer.lock().unwrap_or_else(|e| e.into_inner()).write("b");
}

Rust struct within struct: borrowing, lifetime, generic types and more total confusion

I'm trying to modify an existing application that forces me to learn rust and it's giving me a hard time (reformulating...)
I would like to have a struct with two fields:
pub struct Something<'a> {
pkt_wtr: PacketWriter<&'a mut Vec<u8>>,
buf: Vec<u8>,
}
Where 'buf' will be used as an io for PacketWriter to write its results. So PacketWriter is something like
use std::io::{self};
pub struct PacketWriter<T :io::Write> {
wtr :T,
}
impl <T :io::Write> PacketWriter<T> {
pub fn new(wtr :T) -> Self {
return PacketWriter {
wtr,
};
}
pub fn into_inner(self) -> T {
self.wtr
}
pub fn write(&mut self) {
self.wtr.write_all(&[10,11,12]).unwrap();
println!("wrote packet");
}
}
Then inside 'Something' I want to use PacketWriter this way: let it write what it needs in 'buf' and drain it by pieces.
impl Something<'_> {
pub fn process(&mut self) {
self.pkt_wtr.write();
let c = self.buf.drain(0..1);
}
}
What seems to be impossible is to create a workable constructor for 'Something'
impl Something<'_> {
pub fn new() -> Self {
let mut buf = Vec::new();
let pkt_wtr = PacketWriter::new(&mut buf);
return Something {
pkt_wtr: pkt_wtr,
buf: buf,
};
}
}
What does not seem to be doable is, however I try, to have PacketWriter being constructed on a borrowed reference from 'buf' while 'buf' is also stored in the 'Something' object.
I can give 'buf' fully to 'PacketWriter' (per example below) but I cannot then access the content of 'buf' later. I know that it works in the example underneath, but it's because I can have access to the 'buf' after it is given to the "PacketWriter' (through 'wtr'). In reality, the 'PacketWriter' has that field (wtr) private and in addition it's a code that I cannot modify to, for example, obtain a getter for 'wtr'
Thanks
I wrote a small working program to describe the intent and the problem, with the two options
use std::io::{self};
pub struct PacketWriter<T :io::Write> {
wtr :T,
}
impl <T :io::Write> PacketWriter<T> {
pub fn new(wtr :T) -> Self {
return PacketWriter {
wtr,
};
}
pub fn into_inner(self) -> T {
self.wtr
}
pub fn write(&mut self) {
self.wtr.write_all(&[10,11,12]).unwrap();
println!("wrote packet");
}
}
/*
// that does not work of course because buf is local but this is not the issue
pub struct Something<'a> {
pkt_wtr: PacketWriter<&'a mut Vec<u8>>,
buf: Vec<u8>,
}
impl Something<'_> {
pub fn new() -> Self {
let mut buf = Vec::new();
let pkt_wtr = PacketWriter::new(&mut buf);
//let mut pkt_wtr = PacketWriter::new(buf);
return Something {
pkt_wtr,
buf,
};
}
pub fn process(&mut self) {
self.pkt_wtr.write();
println!("process {:?}", self.buf);
}
}
*/
pub struct Something {
pkt_wtr: PacketWriter<Vec<u8>>,
}
impl Something {
pub fn new() -> Self {
let pkt_wtr = PacketWriter::new(Vec::new());
return Something {
pkt_wtr,
};
}
pub fn process(&mut self) {
self.pkt_wtr.write();
let file = &mut self.pkt_wtr.wtr;
println!("processing Something {:?}", file);
let c = file.drain(0..1);
println!("Drained {:?}", c);
}
}
fn main() -> std::io::Result<()> {
let mut file = Vec::new();
let mut wtr = PacketWriter::new(&mut file);
wtr.write();
println!("Got data {:?}", file);
{
let c = file.drain(0..2);
println!("Drained {:?}", c);
}
println!("Remains {:?}", file);
let mut data = Something::new();
data.process();
Ok(())
}
It's not totally clear what the question is, given that the code appears to compile, but I can take a stab at one part: why can't you use into_inner() on self.wtr inside the process function?
into_inner takes ownership of the PacketWriter that gets passed into its self parameter. (You can tell this because the parameter is spelled self, rather than &self or &mut self.) Taking ownership means that it is consumed: it cannot be used anymore by the caller and the callee is responsible for dropping it (read: running destructors). After taking ownership of the PacketWriter, the into_inner function returns just the wtr field and drops (runs destructors on) the rest. But where does that leave the Something struct? It has a field that needs to contain a PacketWriter, and you just took its PacketWriter away and destroyed it! The function ends, and the value held in the PacketWriter field is unknown: it can't be thing that was in there from the beginning, because that was taken over by into_inner and destroyed. But it also can't be anything else.
Rust generally forbids structs from having uninitialized or undefined fields. You need to have that field defined at all times.
Here's the worked example:
pub fn process(&mut self) {
self.pkt_wtr.write();
// There's a valid PacketWriter in pkt_wtr
let raw_wtr: Vec<u8> = self.pkt_wtr.into_inner();
// The PacketWriter in pkt_wtr was consumed by into_inner!
// We have a raw_wtr of type Vec<u8>, but that's not the right type for pkt_wtr
// We could try to call this function here, but what would it do?
self.pkt_wtr.write();
println!("processing Something");
}
(Note: The example above has slightly squishy logic. Formally, because you don't own self, you can't do anything that would take ownership of any part of it, even if you put everything back neatly when you're done.)
You have a few options to fix this, but with one major caveat: with the public interface you have described, there is no way to get access to the PacketWriter::wtr field and put it back into the same PacketWriter. You'll have to extract the PacketWriter::wtr field and put it into a new PacketWriter.
Here's one way you could do it. Remember, the goal is to have self.packet_wtr defined at all times, so we'll use a function called mem::replace to put a dummy PacketWriter into self.pkt_wtr. This ensures that self.pkt_wtr always has something in it.
pub fn process(&mut self) {
self.pkt_wtr.write();
// Create a new dummy PacketWriter and swap it with self.pkt_wtr
// Returns an owned version of pkt_wtr that we're free to consume
let pkt_wtr_owned = std::mem::replace(&mut self.pkt_wtr, PacketWriter::new(Vec::new()));
// Consume pkt_wtr_owned, returning its wtr field
let raw_wtr = pkt_wtr_owned.into_inner();
// Do anything you want with raw_wtr here -- you own it.
println!("The vec is: {:?}", &raw_wtr);
// Create a new PacketWriter with the old PacketWriter's buffer.
// The dummy PacketWriter is dropped here.
self.pkt_wtr = PacketWriter::new(raw_wtr);
println!("processing Something");
}
Rust Playground
This solution is definitely a hack, and it's potentially a place where the borrow checker could be improved to realize that leaving a field temporarily undefined is fine, as long as it's not accessed before it is assigned again. (Though there may be an edge case I missed; this stuff is hard to reason about in general.) Additionally, this is the kind of thing that can be optimized away by later compiler passes through dead store elimination.
If this turns out to be a hotspot when profiling, there are unsafe techniques that would allow the field to be invalid for that period, but that would probably need a new question.
However, my recommendation would be to find a way to get an "escape hatch" function added to PacketWriter that lets you do exactly what you want to do: get a mutable reference to the inner wtr without taking ownership of PacketWriter.
impl<T: io::Write> PacketWriter<T> {
pub fn inner_mut(&mut self) -> &mut T {
&mut self.wtr
}
}
For clarification, I found a solution using Rc+RefCell or Arc+Mutex. I encapsulated the buffer in a Rc/RefCell and added a Write
pub struct WrappedWriter {
data :Arc<Mutex<Vec<u8>>>,
}
impl WrappedWriter {
pub fn new(data : Arc<Mutex<Vec<u8>>>) -> Self {
return WrappedWriter {
data,
};
}
}
impl Write for WrappedWriter {
fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
let mut data = self.data.lock().unwrap();
data.write(buf)
}
fn flush(&mut self) -> Result<(), Error> {
Ok(())
}
}
pub struct Something {
wtr: PacketWriter<WrappedWriter>,
data : Arc<Mutex<Vec<u8>>>,
}
impl Something {
pub fn new() -> Result<Self, Error> {
let data :Arc<Mutex<Vec<u8>>> = Arc::new(Mutex::new(Vec::new()));
let wtr = PacketWriter::new(WrappedWriter::new(Arc::clone(&data)));
return Ok(PassthroughDecoder {
wtr,
data,
});
}
pub fn process(&mut self) {
let mut data = self.data.lock().unwrap();
data.clear();
}
}
You can replace Arc by Rc and Mutex by RefCell if you don't have thread-safe issues in which case the reference access becomes
let data = self.data.borrow_mut();

Initializing a FnMut member variable with a static Fn function

Problem Description
I have a Config struct that can store a FnMut callback function. The catch is: not all of my configurations require a callback function, so I would like to make adding a callback function optional. This requires the member variable to be initialized with a default function that will get used if no callback is set.
Existing Code
struct Config<'a>{
callback: &'a mut dyn (FnMut(&str))
}
fn default_fn(msg: &str){
println!("default_fn({})", msg);
}
impl<'a> Config<'a> {
pub fn new() -> Config<'a> {
Config{
callback: &default_fn // ERROR: types differ in mutability
}
}
pub fn set_callback(mut self, callback_fn: &'a mut dyn (FnMut(&str))) -> Config<'a> {
self.callback = callback_fn;
self
}
}
fn main() {
// Our FnMut callback
let mut msg_log: Vec<String> = vec![];
let mut callback_fn = |msg: &str| {
msg_log.push(msg.to_string());
};
{
let mut config = Config::new();
(config.callback)("Hello World!");
config = config.set_callback(&mut callback_fn);
(config.callback)("Hello World!");
}
// Demonstration that the callback actually works
println!("{:?}", msg_log);
}
error[E0308]: mismatched types
--> src/main.rs:13:23
|
13 | callback: &default_fn // ERROR: types differ in mutability
| ^^^^^^^^^^^ types differ in mutability
|
= note: expected type `&mut dyn for<'r> std::ops::FnMut(&'r str)`
found type `&for<'r> fn(&'r str) {default_fn}`
Does someone have any suggestions on how to solve that problem?
Things I already tried, without any success:
Initializing it with a closure: callback: &|_: &str|{}
Using a member function instead of a global function
Creating a mutable reference: callback: &mut default_fn
(causes: cannot return value referencing temporary value)
I'm running out of ideas, any help is appreciated. Even if the answer is that what I am trying to do is impossible for reasons I didn't realize yet.
You should really box the trait object function. That makes the whole code much easier to use:
struct Config<'a>{
callback: Box<dyn FnMut(&str) + 'a>,
}
fn default_fn(msg: &str){
println!("default_fn({})", msg);
}
impl<'a> Config<'a> {
pub fn new() -> Config<'a> {
Config{
callback: Box::new(default_fn)
}
}
pub fn set_callback(self, callback: &'a mut dyn (FnMut(&str))) -> Config<'a> {
Config {
callback: Box::new(callback),
..self
}
}
}
fn main() {
// Our FnMut callback
let mut msg_log = vec![];
let mut callback_fn = |msg: &str| {
msg_log.push(msg.to_string());
};
{
let mut config = Config::new();
(config.callback)("Hello World!");
config = config.set_callback(&mut callback_fn);
(config.callback)("Hello World!");
}
// Demonstration that the callback actually works
println!("{:?}", msg_log);
}
Note that it is difficult to use callbacks in idiomatic Rust. I would even say that they aren't idiomatic at all. You should use a channel, something like that:
use std::sync::mpsc::{channel, Sender, SendError};
struct Config {
sender: Sender<String>,
}
impl Config {
pub fn new(sender: Sender<String>) -> Config {
Config{
sender
}
}
pub fn send(&self, message: String) -> Result<(), SendError<String>> {
self.sender.send(message)
}
}
fn main() {
let (sender, receiver) = channel();
let config = Config::new(sender);
config.send("Hello world!".into()).unwrap();
println!("{:?}", receiver.recv().unwrap());
}
Just wanted to share the solution I found:
Single-threaded, callback-based.
While in my opinion this one really answers the question I had, I think you guys are still right about the problems I might encounter in the future with this programming style. I will definitely reconsider your advice about using channels.
struct Config<'a>{
callback: Option<&'a mut dyn (FnMut(&str))>
}
impl<'a> Config<'a> {
pub fn new() -> Config<'a> {
Config{
callback: None
}
}
pub fn set_callback(mut self, callback_fn: &'a mut dyn (FnMut(&str))) -> Config<'a> {
self.callback = Some(callback_fn);
self
}
pub fn run_callback(&mut self, msg: &str){
if let Some(callback) = &mut self.callback{
callback(msg);
} else {
// Default code
println!("default_fn({})", msg);
}
}
}
fn main() {
// Our FnMut callback
let mut msg_log: Vec<String> = vec![];
let mut callback_fn = |msg: &str| {
msg_log.push(msg.to_string());
};
let mut config = Config::new();
config.run_callback("Hello World!");
config = config.set_callback(&mut callback_fn);
config.run_callback("Hello World!");
// Demonstration that the callback actually works
println!("{:?}", msg_log);
}

Resources