Mutable function

How do I set a mutable function from inside a closure?

Can you try to further describe what do you want to do? Help others help you.

let mut func = || {
stuff()
}
{
func = || { otherstuff() }
}

Each || ... closure has a different type from all others, which is why simple assignment fails here. You need to convert the closures to a different type. You can convert them to function pointers, if they don't capture anything:

let mut func: fn() = || stuff();
func = || otherstuff();

But if the closures capture any variables then you must resort to the more general Box<dyn Fn()>, Box<dyn FnMut()>, or Box<dyn FnOnce()> (depending on whether the closure mutates or moves one of its owned types):

let mut func: Box<dyn FnMut()> = Box::new(|| stuff());
func = Box::new(|| otherstuff());

You can read more about the different function traits in The Rust Programming Language chapter 13, and about dyn (trait objects) in chapter 17.

5 Likes
fn stuff() {}


fn otherstuff() {}

fn main() {
  let mut func: Box<dyn FnOnce() -> ()> = Box::new(|| {
    stuff()
  });
  
  {
    func = Box::new(|| { 
      otherstuff() 
    });
  }
}

The compiler forces me to do something like this :
paintmode.as_deref_mut() = Some(&mut Box::new(
and yet tells me that it's an invalid left hand assignment

Show us the actual full error message from the compiler (from running cargo check), and the code (at least an entire function) that produced it. We need to see the situation you're in to be able to tell you what's going wrong.

error[E0070]: invalid left-hand side of assignment
--> src\main.rs:37:38
|
37 | paintmode.as_deref_mut() = Some(&mut Box::new(|| {
| ------------------------ ^
| |
| cannot assign to this expression

For more information about this error, try rustc --explain E0070.
error: could not compile Art (bin "Art") due to previous error

I'm guessing paintmode is something containing an Option. Is it an &mut Option<...>? In that case, what you need to write is

*paintmode = Some(Box::new(|| { ... }));

Note the asterisk — that is necessary when assigning through a mutable reference.

If that is not in fact the problem, please show us your code, at least an entire function. We can't be confident in what the problem is if we can't see the types involved.

1 Like

Options literally can't be dereferenced

Please show your entire function code. Otherwise we can only keep guessing and getting it wrong.

3 Likes

*paintmode = Some(Box::from(|| {
let app = weak.unwrap();
let tup = &mut images[app.get_current_image() as usize];
let (mut image, mut pixmap) = (&mut tup.0, &mut tup.1);
let (width, height) = (image.width(), image.height());
for i in 0..height {
for e in 0..width {
pixmap.fill(tiny_skia::Color::BLACK)
}
}
}))
}
using slint and tiny_skia

  1. Please show the entire code of the function that executes this assignment (not only the function you're assigning). We don't know what is paintmode, for example, and probably will uncover some more questions which will all be answered by simply copy-pasting the whole erroneous example.
  2. Please use code formatting, as in the pinned topic, so that nothing is lost by markup.
use anyhow::Result;
use slint::{Image, RenderingState, Rgba8Pixel, SharedPixelBuffer};
slint::include_modules!();
use slint::private_unstable_api::re_exports::EventResult;
use std::borrow::Cow;
use tiny_skia::PixmapMut;
fn main() -> Result<()> {
    let app = App::new().unwrap();
    let weak = app.as_weak();
    let mut paintmode: Option<Box<dyn FnMut()>> = Some(Box::new(|| {}));
    let mut images: Vec<(SharedPixelBuffer<Rgba8Pixel>, PixmapMut)> = vec![];
    let mut img = SharedPixelBuffer::<Rgba8Pixel>::new(32, 32);
    let mut pixmap = PixmapMut::from_bytes(img.make_mut_bytes(), 32, 32).unwrap();
    pixmap.fill(tiny_skia::Color::BLACK);
    app.on_key_pressed(move |key| {
        let app = weak.unwrap();
        EventResult::Accept
    });
    app.on_draw(move || {
        if let Some(mut function) = paintmode.take() {
            function()
        }
    });
    app.window()
        .set_rendering_notifier(move |state, _graphics_api| {
            let app = weak.unwrap();
            match state {
                RenderingState::BeforeRendering => app.set_canvas_source(
                    Image::from_rgba8_premultiplied(Cow::Borrowed(&img).into_owned()),
                ),
                _ => {}
            }
        })
        .expect("Couldn't add rendering notifier");
    app.on_ctc(move |value| match value {
        Mode::Lighting => {
            *paintmode = Some(Box::from(|| {
                let app = weak.unwrap();
                let tup = &mut images[app.get_current_image() as usize];
                let (mut image, mut pixmap) = (&mut tup.0, &mut tup.1);
                let (width, height) = (image.width(), image.height());
                for i in 0..height {
                    for e in 0..width {
                        pixmap.fill(tiny_skia::Color::BLACK)
                    }
                }
            }))
        }
        Mode::Paint => {}
        _ => {}
    });
    app.run().unwrap();
    Ok(())
}
#[non_exhaustive]
struct Mode;
impl Mode {
    pub const Lighting: i32 = 0;
    pub const Paint: i32 = 1;
}

So this simplifies to the situation:

let mut foo = None;

bar(|| {
  foo = baz();
});

If the parameter to bar is FnMut or FnOnce, then this works fine, because all uses of the outer scope are captured by mutable reference.

But if you need the callback to be a move so that it can outlive the outer scope, then that doesn't work, because you just moved it out! (And also you're trying to move it out twice in both on_draw and on_ctc, which won't work unless the type is Copy, and then it doesn't do what you expect)

So I guess try just removing the dereference (the *) and the moves if you can, but otherwise you're dealing with shared mutable state, which is going to be a pain.

Check how to handle state with slint, possibly with Global in slint - Rust ?