Ok what is happening here someone explain?

here's my struct

pub struct Action <'a> {
  label: &'a str,
  action_fn: &'a dyn Fn() -> Return
}

pub struct Menu <'a> {
  heading: &'a str,
  list: Vec<MenuItem<'a>>,
  cursor: u16
}

pub struct SubMenu <'a> {
  label: &'a str,
  menu: Menu<'a>
}

pub enum MenuItem <'a> {
  SubMenu(SubMenu<'a>),
  Action(Action<'a>)
}

And I have some methods defined for Menu. So I can use them as follows.

let mut menu_map = Menu::new("Main Menu")
                .sub_menu("Start",
                  Menu::new("start new game")
                  .sub_menu("opt0",
                    Menu::new("hehehehaw")
                    .action("all the way back", &|| -> Return { Return::All })
                    .back("bakc")
                  )
                  .action("opt1", &|| -> Return { Return::None })
                  .back("back")
                )
                .back("quit");

Everything is working as expected (I am storing the functions in a Vec<MenuItem<'a>> so that I can call it by accessing the list).

My question is what's happening with the closures &|| { Return::All } and &|| { Return::None }

What issues will I face if I keep using closure-reference-thing?

What do you mean by "what's happening with them"? In the code you presented so far, all that happens with the closures is that they get stored in your data structure.

1 Like

I think there are two reasons why the expression &|…| { … } can work:

The latter can only work until the end of the block. Consider this:

struct A<'a> {
    closure: &'a dyn Fn(),
}

fn foo() -> A<'static> {
    /*
    let x = 2;
    A { closure: &|| println!("Foo {x}") }
    */
    A { closure: &|| println!("Foo") }
}

fn main() {
    let x = 1;
    let a = A { closure: &|| println!("Main {x}") };
    (a.closure)();
    (foo().closure)();
}

(Playground)

Output:

Main 1
Foo

The commented-out code would not compile, but the rest does compile and run.


This is as closest as you can get, if you try to use the reference beyond the block (function scope?):

fn foo() -> A<'static> {
    let x = 2;
    A { closure: &move || println!("Foo {x}") }
}

(Playground)

Errors:

   Compiling playground v0.0.1 (/playground)
error[E0515]: cannot return value referencing temporary value
 --> src/main.rs:7:5
  |
7 |     A { closure: &move || println!("Foo {x}") }
  |     ^^^^^^^^^^^^^^---------------------------^^
  |     |             |
  |     |             temporary value created here
  |     returns a value referencing data owned by the current function

For more information about this error, try `rustc --explain E0515`.
error: could not compile `playground` due to previous error


This works, in turn:

struct A<'a> {
    closure: &'a dyn Fn(),
}

fn main() {
    let a = {
        let x = 3;
        A { closure: &move || println!("{x}") }
    };
    (a.closure)();
}

(Playground)

Output:

3

That code doesn't seem right to my eyes for some reason.

I tried to store it as Fn() -> Return instead of &'a dyn Fn() -> Return but complier throw an error as closure goes out of scope when the method back ends (which makes sense).

impl Menu {
  pub fn back(mut self, label: &'a str) -> Self {
    self.list.push(MenuItem::Action(Action{label, action_fn: || -> Return { Return::Once }}));
    self
  }
}

I tried to do javascript equivalent

function push_fn(arr) {
  arr.push(() => console.log("yes"));
}

let arr = [];
push_fn(arr);

but in rust it doesn't work that way I think

Yes that makes sense I guess. So far I am facing no problems.

When a closure captures nothing, it can be converted to a bare function, which in turn means that it can be promoted to have the static lifetime, hence references to it can be valid forever. You can search for "rust static promotion" for the details and exact rules.

3 Likes

Ya this is what I was looking for. Thanks for the info👍

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.