Use of moved value and cannot assign to `X`, as it is a captured variable in a `Fn` closure

Hello

I have made a few buttons in OrbTk and a string. If a button is clicked the value of that button must be appended to that string.

use orbtk::*;
use std::vec::Vec;
use std::cell::RefCell;
use std::rc::Rc;
use std::io::Write;
use std::str::FromStr;

extern crate strum;
#[macro_use]
extern crate strum_macros;

widget!(MainView);

// Enumeration of all possible operators
#[derive(EnumString)]
enum Operator {
    #[strum(serialize="+")]
    Plus,
    #[strum(serialize="-")]
    Min,
    #[strum(serialize="*")]
    Times,
    #[strum(serialize="/")]
    Divide,
}


impl Template for MainView {
    fn template(self, _: Entity, ctx: &mut BuildContext) -> Self {

        // String holding pressed keys creating an infix string
        let mut pressedKeys : String = "".to_string();

        // Initializing Grid
        let mut grid = Grid::create(); 

        // Configuring grid (amount of rows and columns)
        grid = grid
            .columns(
                Columns::create()
                    .column("*")
                    .column("*")
                    .column("*")
                    .column("*")
                    .build()
            )
            .rows(
                Rows::create()
                    .row(50.0)
                    .row(50.0)
                    .row(50.0)
                    .row(50.0)
                    .row(50.0)
                    .row(50.0)
                    .build()
            );
        
        // Adding all buttons from 1-9 to the grid
        // in calculator format
        let mut counter : u8 = 9;
        for i in 0..3 {
            for j in 0..3 {
                grid = grid.child(
                    Button::create()
                        .text(counter.to_string())
                        .attach(Grid::column(2-j))
                        .attach(Grid::row(i))
                        .on_click({
                            move |_states, _| -> bool {
                                pressedKeys = format!("{}{}", pressedKeys, counter.to_string());
                                true
                            }
                        })
                        .build(ctx)
                );

                counter = counter - 1;
            }
        }

        self.name("MainView").child(
            grid.build(ctx)
        )
   } 
}

fn main() {
    Application::new()
        .window(|ctx| {
            Window::create()
                .title("OrbTk - Calculator")
                .position((100.0, 100.0))
                .size(420.0, 500.0)
                .child(MainView::create().build(ctx))
                .build(ctx)
        })
        .run();
}

Cargo:

[package]
name = "orb_tk_calculator"
version = "0.1.0"
authors = ["ONiel"]
edition = "2018"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
orbtk = { git = "https://github.com/redox-os/orbtk.git", branch = "develop" }

strum = "0.18.0"
strum_macros = "0.18.0"

I think my code is correct, but Rust doesn't agree:

warning: unused import: `std::io::Write`
 --> src/main.rs:5:5
  |
5 | use std::io::Write;
  |     ^^^^^^^^^^^^^^
  |
  = note: `#[warn(unused_imports)]` on by default

warning: unused import: `std::str::FromStr`
 --> src/main.rs:6:5
  |
6 | use std::str::FromStr;
  |     ^^^^^^^^^^^^^^^^^

error[E0594]: cannot assign to `pressedKeys`, as it is a captured variable in a `Fn` closure
  --> src/main.rs:70:33
   |
70 | ...                   pressedKeys = format!("{}{}", pressedKeys, counter.to_string());
   |                       ^^^^^^^^^^^ cannot assign
   |
help: consider changing this to accept closures that implement `FnMut`
  --> src/main.rs:69:29
   |
69 | / ...                   move |_states, _| -> bool {
70 | | ...                       pressedKeys = format!("{}{}", pressedKeys, counter.to_string());
71 | | ...                       true
72 | | ...                   }
   | |_______________________^

warning: variable does not need to be mutable
  --> src/main.rs:32:13
   |
32 |         let mut pressedKeys : String = "".to_string();
   |             ----^^^^^^^^^^^
   |             |
   |             help: remove this `mut`
   |
   = note: `#[warn(unused_mut)]` on by default

error[E0382]: use of moved value: `pressedKeys`
  --> src/main.rs:69:29
   |
32 |         let mut pressedKeys : String = "".to_string();
   |             --------------- move occurs because `pressedKeys` has type `std::string::String`, which does not implement the `Copy` trait
...
69 |                             move |_states, _| -> bool {
   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^ value moved into closure here, in previous iteration of loop
70 |                                 pressedKeys = format!("{}{}", pressedKeys, counter.to_string());
   |                                                               ----------- use occurs due to use in closure

I get one error less when I use the let keyword like this:

let pressedKeys = format!("{}{}", pressedKeys, counter.to_string());

But I think this isn't a good solution because this isn't updating the original pressedKeys variable but is making a new local one?

Anyway, thanks!

Would I have to structure my code differently? Or use Rc? How exactly?

You get the error, because have 1 copy of a string, and but you're trying to create N closures where each is given exclusive ownership (move) of the same string. You can't have N closures all have exclusive ownership of the same thing.

The solution is to .clone() the string right before it's moved into the closure.

1 Like

Like this?

                        .on_click({
                            let mut pressed_keys = pressed_keys.clone();
                            move |_states, _| -> bool {
                                pressed_keys = format!("{}{}", pressed_keys, counter.to_string());
                                true
                            }
                        })

I tried making a static String. That wasn't possible, seems that only str can be made static.

use orbtk::*;
use std::vec::Vec;
use std::cell::RefCell;
use std::rc::Rc;
use std::io::Write;
use std::str::FromStr;

extern crate strum;
#[macro_use]
extern crate strum_macros;

widget!(MainView);

// Enumeration of all possible operators
#[derive(EnumString)]
enum Operator {
    #[strum(serialize="+")]
    Plus,
    #[strum(serialize="-")]
    Min,
    #[strum(serialize="*")]
    Times,
    #[strum(serialize="/")]
    Divide,
}


// String holding pressed keys creating an infix string
static mut pressed_keys : &str = "";

impl Template for MainView {
    fn template(self, _: Entity, ctx: &mut BuildContext) -> Self {


        // Initializing Grid
        let mut grid = Grid::create(); 

        // Configuring grid (amount of rows and columns)
        grid = grid
            .columns(
                Columns::create()
                    .column("*")
                    .column("*")
                    .column("*")
                    .column("*")
                    .build()
            )
            .rows(
                Rows::create()
                    .row(50.0)
                    .row(50.0)
                    .row(50.0)
                    .row(50.0)
                    .row(50.0)
                    .row(50.0)
                    .build()
            );
        
        // Adding all buttons from 1-9 to the grid
        // in calculator format
        let mut counter : u8 = 9;
        for i in 0..3 {
            for j in 0..3 {

                grid = grid.child(
                    Button::create()
                        .text(counter.to_string())
                        .attach(Grid::column(2-j))
                        .attach(Grid::row(i))
                        .on_click({
                            move |_states, _| -> bool {
                                unsafe {
                                    pressed_keys = &format!("{}{}", pressed_keys, counter.to_string());
                                }
                                true
                            }
                        })
                        .build(ctx)
                );

                counter = counter - 1;
            }
        }

        self.name("MainView").child(
            grid.build(ctx)
        )
   } 
}

fn main() {
    Application::new()
        .window(|ctx| {
            Window::create()
                .title("OrbTk - Calculator")
                .position((100.0, 100.0))
                .size(420.0, 500.0)
                .child(MainView::create().build(ctx))
                .build(ctx)
        })
        .run();
}

Which gives an error, because I need to use & to convert a String to str on line 73...

...

There's a reason static mut is unsafe. Don't use it. Consider using an Rc<RefCell<String>> to share the string between many places.

1 Like
 // String holding pressed keys creating an infix string
let mut pressed_keys: Rc<RefCell<String>> = Rc::new(RefCell::new("".to_string()));

How do I update it's value though?

You clone it such that you can access it where you need to, then you do this:

*pressed_keys.borrow_mut() = "your_string".to_string();
1 Like
                grid = grid.child(
                    Button::create()
                        .text(counter.to_string())
                        .attach(Grid::column(2-j))
                        .attach(Grid::row(i))
                        .on_click({
                            pressed_keys.clone();
                            move |_states, _| -> bool {
                                *pressed_keys.borrow_mut() = format!("{}{}", *pressed_keys.borrow(), counter.to_string());
                                true
                            }
                        })
                        .build(ctx)
                );

Generates the error

error[E0382]: borrow of moved value: `pressed_keys`
  --> src/main.rs:71:29
   |
33 |         let pressed_keys: Rc<RefCell<String>> = Rc::new(RefCell::new("".to_string()));
   |             ------------ move occurs because `pressed_keys` has type `std::rc::Rc<std::cell::RefCell<std::string::String>>`, which does not implement the `Copy` trait
...
71 |                             pressed_keys.clone();
   |                             ^^^^^^^^^^^^ value borrowed here after move
72 |                             move |_states, _| -> bool {
   |                             ------------------------- value moved into closure here, in previous iteration of loop

You want to actually create a new variable:

let pressed_keys = pressed_keys.clone();
1 Like

Thanks! Made something pretty cool with it.

1 Like