How can I work around not being able to export functions with lifetimes when using wasm-bindgen?












3














I'm trying to write a simple game that runs in the browser, and I'm having a hard time modeling a game loop given the combination of restrictions imposed by the browser, rust, and wasm-bindgen.



A typical game loop in the browser follows this general pattern:



function mainLoop() {
update();
draw();
requestAnimationFrame(mainLoop);
}


If I were to model this exact pattern in rust/wasm-bindgen, it would look like this:



let main_loop = Closure::wrap(Box::new(move || {
update();
draw();
window.request_animation_frame(main_loop.as_ref().unchecked_ref()); // Not legal
}) as Box<FnMut()>);


Unlike javascript, I'm unable to reference main_loop from within itself, so this doesn't work.



An alternative approach that someone suggested is to follow the pattern illustrated in the game of life example. At a high-level, it involves exporting a type that contains the game state and includes public tick() and render() functions that can be called from within a javascript game loop. This doesn't work for me because my gamestate requires lifetime parameters, since it effectively just wraps a specs World and Dispatcher struct, the latter of which has lifetime parameters. Ultimately, this means that I can't export it using #[wasm_bindgen].



I'm having a hard time finding ways to work around these restrictions, and am looking for suggestions.










share|improve this question



























    3














    I'm trying to write a simple game that runs in the browser, and I'm having a hard time modeling a game loop given the combination of restrictions imposed by the browser, rust, and wasm-bindgen.



    A typical game loop in the browser follows this general pattern:



    function mainLoop() {
    update();
    draw();
    requestAnimationFrame(mainLoop);
    }


    If I were to model this exact pattern in rust/wasm-bindgen, it would look like this:



    let main_loop = Closure::wrap(Box::new(move || {
    update();
    draw();
    window.request_animation_frame(main_loop.as_ref().unchecked_ref()); // Not legal
    }) as Box<FnMut()>);


    Unlike javascript, I'm unable to reference main_loop from within itself, so this doesn't work.



    An alternative approach that someone suggested is to follow the pattern illustrated in the game of life example. At a high-level, it involves exporting a type that contains the game state and includes public tick() and render() functions that can be called from within a javascript game loop. This doesn't work for me because my gamestate requires lifetime parameters, since it effectively just wraps a specs World and Dispatcher struct, the latter of which has lifetime parameters. Ultimately, this means that I can't export it using #[wasm_bindgen].



    I'm having a hard time finding ways to work around these restrictions, and am looking for suggestions.










    share|improve this question

























      3












      3








      3







      I'm trying to write a simple game that runs in the browser, and I'm having a hard time modeling a game loop given the combination of restrictions imposed by the browser, rust, and wasm-bindgen.



      A typical game loop in the browser follows this general pattern:



      function mainLoop() {
      update();
      draw();
      requestAnimationFrame(mainLoop);
      }


      If I were to model this exact pattern in rust/wasm-bindgen, it would look like this:



      let main_loop = Closure::wrap(Box::new(move || {
      update();
      draw();
      window.request_animation_frame(main_loop.as_ref().unchecked_ref()); // Not legal
      }) as Box<FnMut()>);


      Unlike javascript, I'm unable to reference main_loop from within itself, so this doesn't work.



      An alternative approach that someone suggested is to follow the pattern illustrated in the game of life example. At a high-level, it involves exporting a type that contains the game state and includes public tick() and render() functions that can be called from within a javascript game loop. This doesn't work for me because my gamestate requires lifetime parameters, since it effectively just wraps a specs World and Dispatcher struct, the latter of which has lifetime parameters. Ultimately, this means that I can't export it using #[wasm_bindgen].



      I'm having a hard time finding ways to work around these restrictions, and am looking for suggestions.










      share|improve this question













      I'm trying to write a simple game that runs in the browser, and I'm having a hard time modeling a game loop given the combination of restrictions imposed by the browser, rust, and wasm-bindgen.



      A typical game loop in the browser follows this general pattern:



      function mainLoop() {
      update();
      draw();
      requestAnimationFrame(mainLoop);
      }


      If I were to model this exact pattern in rust/wasm-bindgen, it would look like this:



      let main_loop = Closure::wrap(Box::new(move || {
      update();
      draw();
      window.request_animation_frame(main_loop.as_ref().unchecked_ref()); // Not legal
      }) as Box<FnMut()>);


      Unlike javascript, I'm unable to reference main_loop from within itself, so this doesn't work.



      An alternative approach that someone suggested is to follow the pattern illustrated in the game of life example. At a high-level, it involves exporting a type that contains the game state and includes public tick() and render() functions that can be called from within a javascript game loop. This doesn't work for me because my gamestate requires lifetime parameters, since it effectively just wraps a specs World and Dispatcher struct, the latter of which has lifetime parameters. Ultimately, this means that I can't export it using #[wasm_bindgen].



      I'm having a hard time finding ways to work around these restrictions, and am looking for suggestions.







      rust webassembly wasm-bindgen






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked Oct 26 '18 at 1:58









      w.brianw.brian

      6,97654180




      6,97654180
























          2 Answers
          2






          active

          oldest

          votes


















          5














          The easiest way to model this is likely to leave invocations of requestAnimationFrame to JS and instead just implement the update/draw logic in Rust.



          In Rust, however, what you can also do is to exploit the fact that a closure which doesn't actually capture any variables is zero-size, meaning that Closure<T> of that closure won't allocate memory and you can safely forget it. For example something like this should work:



          #[wasm_bindgen]
          pub fn main_loop() {
          update();
          draw();
          let window = ...;
          let closure = Closure::wrap(Box::new(|| main_loop()) as Box<Fn()>);
          window.request_animation_frame(closure.as_ref().unchecked_ref());
          closure.forget(); // not actually leaking memory
          }


          If your state has lifetimes inside of it, that is unfortunately incompatible with returning back to JS because when you return all the way back to the JS event loop then all WebAssembly stack frames have been popped, meaning that any lifetime is invalidated. This means that your game state persisted across iterations of the main_loop will need to be 'static






          share|improve this answer























          • This definitely got me closer, but I'm running into a different issue now. I created a Game struct that wraps any types that require 'static lifetimes to be declared. I changed the main_loop signature to fn main_loop(mut game: Game) and have the closure move game. This doesn't work because now the closure can't be wrapped by a wasm Closure. It would seem the only other option is to use unsafe and declare a static mut to hold the game state. Or is there another cleaner solution still?
            – w.brian
            Nov 2 '18 at 1:08












          • Hm I'm not sure I quite follow what isn't working with the new game: Game, do you have a gist of the error message for why it can't be wrapped in a wasm Closure?
            – alexcrichton
            Nov 2 '18 at 21:28










          • The issue is that by adding move the closure becomes a FnOnce, which is incompatible with Closure::wrap(). This makes sense given that there is no way to statically guarantee that a FnOnce wrapped by by Closure is actually only run once in js land. By the way, I really appreciate your help!
            – w.brian
            Nov 3 '18 at 17:30












          • Oh right yeah, I see! For that you can work around it for now by using an Option<T> followed by take to let it be a FnMut closure (but panic if invoked twice). This is something we should add support for in wasm-bindgen as well! (FnOnce closures)
            – alexcrichton
            Nov 5 '18 at 15:33





















          1














          I'm a Rust novice, but here's how I addressed the same issue.



          You can eliminate the problematic window.request_animation_frame recursion and implement an FPS cap at the same time by invoking window.request_animation_frame from a window.set_interval callback which checks a Rc<RefCell<bool>> or something to see if there's an animation frame request still pending. I'm not sure if the inactive tab behavior will be any different in practice.



          I put the bool into my application state since I'm using an Rc<RefCell<...>> to that anyway for other event handling. I haven't checked that this below compiles as is, but here's the relevant parts of how I'm doing this:



          pub struct MyGame {
          ...
          should_request_render: bool, // Don't request another render until the previous runs, init to false since we'll fire the first one immediately.
          }

          ...

          let window = web_sys::window().expect("should have a window in this context");
          let application_reference = Rc::new(RefCell::new(MyGame::new()));

          let request_animation_frame = { // request_animation_frame is not forgotten! Its ownership is moved into the timer callback.
          let application_reference = application_reference.clone();
          let request_animation_frame_callback = Closure::wrap(Box::new(move || {
          let mut application = application_reference.borrow_mut();
          application.should_request_render = true;
          application.handle_animation_frame(); // handle_animation_frame being your main loop.
          }) as Box<FnMut()>);
          let window = window.clone();
          move || {
          window
          .request_animation_frame(
          request_animation_frame_callback.as_ref().unchecked_ref(),
          )
          .unwrap();
          }
          };
          request_animation_frame(); // fire the first request immediately

          let timer_closure = Closure::wrap(
          Box::new(move || { // move both request_animation_frame and application_reference here.
          let mut application = application_reference.borrow_mut();
          if application.should_request_render {
          application.should_request_render = false;
          request_animation_frame();
          }
          }) as Box<FnMut()>
          );
          window.set_interval_with_callback_and_timeout_and_arguments_0(
          timer_closure.as_ref().unchecked_ref(),
          25, // minimum ms per frame
          )?;
          timer_closure.forget(); // this leaks it, you could store it somewhere or whatever, depends if it's guaranteed to live as long as the page


          You can store the result of set_interval and the timer_closure in Options in your game state so that your game can clean itself up if needed for some reason (maybe? I haven't tried this, and it would seem to cause a free of self?). The circular reference won't erase itself unless broken (you're then storing Rcs to the application inside the application effectively). It should also enable you to change the max fps while running, by stopping the interval and creating another using the same closure.






          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53000413%2fhow-can-i-work-around-not-being-able-to-export-functions-with-lifetimes-when-usi%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            5














            The easiest way to model this is likely to leave invocations of requestAnimationFrame to JS and instead just implement the update/draw logic in Rust.



            In Rust, however, what you can also do is to exploit the fact that a closure which doesn't actually capture any variables is zero-size, meaning that Closure<T> of that closure won't allocate memory and you can safely forget it. For example something like this should work:



            #[wasm_bindgen]
            pub fn main_loop() {
            update();
            draw();
            let window = ...;
            let closure = Closure::wrap(Box::new(|| main_loop()) as Box<Fn()>);
            window.request_animation_frame(closure.as_ref().unchecked_ref());
            closure.forget(); // not actually leaking memory
            }


            If your state has lifetimes inside of it, that is unfortunately incompatible with returning back to JS because when you return all the way back to the JS event loop then all WebAssembly stack frames have been popped, meaning that any lifetime is invalidated. This means that your game state persisted across iterations of the main_loop will need to be 'static






            share|improve this answer























            • This definitely got me closer, but I'm running into a different issue now. I created a Game struct that wraps any types that require 'static lifetimes to be declared. I changed the main_loop signature to fn main_loop(mut game: Game) and have the closure move game. This doesn't work because now the closure can't be wrapped by a wasm Closure. It would seem the only other option is to use unsafe and declare a static mut to hold the game state. Or is there another cleaner solution still?
              – w.brian
              Nov 2 '18 at 1:08












            • Hm I'm not sure I quite follow what isn't working with the new game: Game, do you have a gist of the error message for why it can't be wrapped in a wasm Closure?
              – alexcrichton
              Nov 2 '18 at 21:28










            • The issue is that by adding move the closure becomes a FnOnce, which is incompatible with Closure::wrap(). This makes sense given that there is no way to statically guarantee that a FnOnce wrapped by by Closure is actually only run once in js land. By the way, I really appreciate your help!
              – w.brian
              Nov 3 '18 at 17:30












            • Oh right yeah, I see! For that you can work around it for now by using an Option<T> followed by take to let it be a FnMut closure (but panic if invoked twice). This is something we should add support for in wasm-bindgen as well! (FnOnce closures)
              – alexcrichton
              Nov 5 '18 at 15:33


















            5














            The easiest way to model this is likely to leave invocations of requestAnimationFrame to JS and instead just implement the update/draw logic in Rust.



            In Rust, however, what you can also do is to exploit the fact that a closure which doesn't actually capture any variables is zero-size, meaning that Closure<T> of that closure won't allocate memory and you can safely forget it. For example something like this should work:



            #[wasm_bindgen]
            pub fn main_loop() {
            update();
            draw();
            let window = ...;
            let closure = Closure::wrap(Box::new(|| main_loop()) as Box<Fn()>);
            window.request_animation_frame(closure.as_ref().unchecked_ref());
            closure.forget(); // not actually leaking memory
            }


            If your state has lifetimes inside of it, that is unfortunately incompatible with returning back to JS because when you return all the way back to the JS event loop then all WebAssembly stack frames have been popped, meaning that any lifetime is invalidated. This means that your game state persisted across iterations of the main_loop will need to be 'static






            share|improve this answer























            • This definitely got me closer, but I'm running into a different issue now. I created a Game struct that wraps any types that require 'static lifetimes to be declared. I changed the main_loop signature to fn main_loop(mut game: Game) and have the closure move game. This doesn't work because now the closure can't be wrapped by a wasm Closure. It would seem the only other option is to use unsafe and declare a static mut to hold the game state. Or is there another cleaner solution still?
              – w.brian
              Nov 2 '18 at 1:08












            • Hm I'm not sure I quite follow what isn't working with the new game: Game, do you have a gist of the error message for why it can't be wrapped in a wasm Closure?
              – alexcrichton
              Nov 2 '18 at 21:28










            • The issue is that by adding move the closure becomes a FnOnce, which is incompatible with Closure::wrap(). This makes sense given that there is no way to statically guarantee that a FnOnce wrapped by by Closure is actually only run once in js land. By the way, I really appreciate your help!
              – w.brian
              Nov 3 '18 at 17:30












            • Oh right yeah, I see! For that you can work around it for now by using an Option<T> followed by take to let it be a FnMut closure (but panic if invoked twice). This is something we should add support for in wasm-bindgen as well! (FnOnce closures)
              – alexcrichton
              Nov 5 '18 at 15:33
















            5












            5








            5






            The easiest way to model this is likely to leave invocations of requestAnimationFrame to JS and instead just implement the update/draw logic in Rust.



            In Rust, however, what you can also do is to exploit the fact that a closure which doesn't actually capture any variables is zero-size, meaning that Closure<T> of that closure won't allocate memory and you can safely forget it. For example something like this should work:



            #[wasm_bindgen]
            pub fn main_loop() {
            update();
            draw();
            let window = ...;
            let closure = Closure::wrap(Box::new(|| main_loop()) as Box<Fn()>);
            window.request_animation_frame(closure.as_ref().unchecked_ref());
            closure.forget(); // not actually leaking memory
            }


            If your state has lifetimes inside of it, that is unfortunately incompatible with returning back to JS because when you return all the way back to the JS event loop then all WebAssembly stack frames have been popped, meaning that any lifetime is invalidated. This means that your game state persisted across iterations of the main_loop will need to be 'static






            share|improve this answer














            The easiest way to model this is likely to leave invocations of requestAnimationFrame to JS and instead just implement the update/draw logic in Rust.



            In Rust, however, what you can also do is to exploit the fact that a closure which doesn't actually capture any variables is zero-size, meaning that Closure<T> of that closure won't allocate memory and you can safely forget it. For example something like this should work:



            #[wasm_bindgen]
            pub fn main_loop() {
            update();
            draw();
            let window = ...;
            let closure = Closure::wrap(Box::new(|| main_loop()) as Box<Fn()>);
            window.request_animation_frame(closure.as_ref().unchecked_ref());
            closure.forget(); // not actually leaking memory
            }


            If your state has lifetimes inside of it, that is unfortunately incompatible with returning back to JS because when you return all the way back to the JS event loop then all WebAssembly stack frames have been popped, meaning that any lifetime is invalidated. This means that your game state persisted across iterations of the main_loop will need to be 'static







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited Nov 2 '18 at 0:27









            w.brian

            6,97654180




            6,97654180










            answered Nov 1 '18 at 20:09









            alexcrichtonalexcrichton

            33112




            33112












            • This definitely got me closer, but I'm running into a different issue now. I created a Game struct that wraps any types that require 'static lifetimes to be declared. I changed the main_loop signature to fn main_loop(mut game: Game) and have the closure move game. This doesn't work because now the closure can't be wrapped by a wasm Closure. It would seem the only other option is to use unsafe and declare a static mut to hold the game state. Or is there another cleaner solution still?
              – w.brian
              Nov 2 '18 at 1:08












            • Hm I'm not sure I quite follow what isn't working with the new game: Game, do you have a gist of the error message for why it can't be wrapped in a wasm Closure?
              – alexcrichton
              Nov 2 '18 at 21:28










            • The issue is that by adding move the closure becomes a FnOnce, which is incompatible with Closure::wrap(). This makes sense given that there is no way to statically guarantee that a FnOnce wrapped by by Closure is actually only run once in js land. By the way, I really appreciate your help!
              – w.brian
              Nov 3 '18 at 17:30












            • Oh right yeah, I see! For that you can work around it for now by using an Option<T> followed by take to let it be a FnMut closure (but panic if invoked twice). This is something we should add support for in wasm-bindgen as well! (FnOnce closures)
              – alexcrichton
              Nov 5 '18 at 15:33




















            • This definitely got me closer, but I'm running into a different issue now. I created a Game struct that wraps any types that require 'static lifetimes to be declared. I changed the main_loop signature to fn main_loop(mut game: Game) and have the closure move game. This doesn't work because now the closure can't be wrapped by a wasm Closure. It would seem the only other option is to use unsafe and declare a static mut to hold the game state. Or is there another cleaner solution still?
              – w.brian
              Nov 2 '18 at 1:08












            • Hm I'm not sure I quite follow what isn't working with the new game: Game, do you have a gist of the error message for why it can't be wrapped in a wasm Closure?
              – alexcrichton
              Nov 2 '18 at 21:28










            • The issue is that by adding move the closure becomes a FnOnce, which is incompatible with Closure::wrap(). This makes sense given that there is no way to statically guarantee that a FnOnce wrapped by by Closure is actually only run once in js land. By the way, I really appreciate your help!
              – w.brian
              Nov 3 '18 at 17:30












            • Oh right yeah, I see! For that you can work around it for now by using an Option<T> followed by take to let it be a FnMut closure (but panic if invoked twice). This is something we should add support for in wasm-bindgen as well! (FnOnce closures)
              – alexcrichton
              Nov 5 '18 at 15:33


















            This definitely got me closer, but I'm running into a different issue now. I created a Game struct that wraps any types that require 'static lifetimes to be declared. I changed the main_loop signature to fn main_loop(mut game: Game) and have the closure move game. This doesn't work because now the closure can't be wrapped by a wasm Closure. It would seem the only other option is to use unsafe and declare a static mut to hold the game state. Or is there another cleaner solution still?
            – w.brian
            Nov 2 '18 at 1:08






            This definitely got me closer, but I'm running into a different issue now. I created a Game struct that wraps any types that require 'static lifetimes to be declared. I changed the main_loop signature to fn main_loop(mut game: Game) and have the closure move game. This doesn't work because now the closure can't be wrapped by a wasm Closure. It would seem the only other option is to use unsafe and declare a static mut to hold the game state. Or is there another cleaner solution still?
            – w.brian
            Nov 2 '18 at 1:08














            Hm I'm not sure I quite follow what isn't working with the new game: Game, do you have a gist of the error message for why it can't be wrapped in a wasm Closure?
            – alexcrichton
            Nov 2 '18 at 21:28




            Hm I'm not sure I quite follow what isn't working with the new game: Game, do you have a gist of the error message for why it can't be wrapped in a wasm Closure?
            – alexcrichton
            Nov 2 '18 at 21:28












            The issue is that by adding move the closure becomes a FnOnce, which is incompatible with Closure::wrap(). This makes sense given that there is no way to statically guarantee that a FnOnce wrapped by by Closure is actually only run once in js land. By the way, I really appreciate your help!
            – w.brian
            Nov 3 '18 at 17:30






            The issue is that by adding move the closure becomes a FnOnce, which is incompatible with Closure::wrap(). This makes sense given that there is no way to statically guarantee that a FnOnce wrapped by by Closure is actually only run once in js land. By the way, I really appreciate your help!
            – w.brian
            Nov 3 '18 at 17:30














            Oh right yeah, I see! For that you can work around it for now by using an Option<T> followed by take to let it be a FnMut closure (but panic if invoked twice). This is something we should add support for in wasm-bindgen as well! (FnOnce closures)
            – alexcrichton
            Nov 5 '18 at 15:33






            Oh right yeah, I see! For that you can work around it for now by using an Option<T> followed by take to let it be a FnMut closure (but panic if invoked twice). This is something we should add support for in wasm-bindgen as well! (FnOnce closures)
            – alexcrichton
            Nov 5 '18 at 15:33















            1














            I'm a Rust novice, but here's how I addressed the same issue.



            You can eliminate the problematic window.request_animation_frame recursion and implement an FPS cap at the same time by invoking window.request_animation_frame from a window.set_interval callback which checks a Rc<RefCell<bool>> or something to see if there's an animation frame request still pending. I'm not sure if the inactive tab behavior will be any different in practice.



            I put the bool into my application state since I'm using an Rc<RefCell<...>> to that anyway for other event handling. I haven't checked that this below compiles as is, but here's the relevant parts of how I'm doing this:



            pub struct MyGame {
            ...
            should_request_render: bool, // Don't request another render until the previous runs, init to false since we'll fire the first one immediately.
            }

            ...

            let window = web_sys::window().expect("should have a window in this context");
            let application_reference = Rc::new(RefCell::new(MyGame::new()));

            let request_animation_frame = { // request_animation_frame is not forgotten! Its ownership is moved into the timer callback.
            let application_reference = application_reference.clone();
            let request_animation_frame_callback = Closure::wrap(Box::new(move || {
            let mut application = application_reference.borrow_mut();
            application.should_request_render = true;
            application.handle_animation_frame(); // handle_animation_frame being your main loop.
            }) as Box<FnMut()>);
            let window = window.clone();
            move || {
            window
            .request_animation_frame(
            request_animation_frame_callback.as_ref().unchecked_ref(),
            )
            .unwrap();
            }
            };
            request_animation_frame(); // fire the first request immediately

            let timer_closure = Closure::wrap(
            Box::new(move || { // move both request_animation_frame and application_reference here.
            let mut application = application_reference.borrow_mut();
            if application.should_request_render {
            application.should_request_render = false;
            request_animation_frame();
            }
            }) as Box<FnMut()>
            );
            window.set_interval_with_callback_and_timeout_and_arguments_0(
            timer_closure.as_ref().unchecked_ref(),
            25, // minimum ms per frame
            )?;
            timer_closure.forget(); // this leaks it, you could store it somewhere or whatever, depends if it's guaranteed to live as long as the page


            You can store the result of set_interval and the timer_closure in Options in your game state so that your game can clean itself up if needed for some reason (maybe? I haven't tried this, and it would seem to cause a free of self?). The circular reference won't erase itself unless broken (you're then storing Rcs to the application inside the application effectively). It should also enable you to change the max fps while running, by stopping the interval and creating another using the same closure.






            share|improve this answer




























              1














              I'm a Rust novice, but here's how I addressed the same issue.



              You can eliminate the problematic window.request_animation_frame recursion and implement an FPS cap at the same time by invoking window.request_animation_frame from a window.set_interval callback which checks a Rc<RefCell<bool>> or something to see if there's an animation frame request still pending. I'm not sure if the inactive tab behavior will be any different in practice.



              I put the bool into my application state since I'm using an Rc<RefCell<...>> to that anyway for other event handling. I haven't checked that this below compiles as is, but here's the relevant parts of how I'm doing this:



              pub struct MyGame {
              ...
              should_request_render: bool, // Don't request another render until the previous runs, init to false since we'll fire the first one immediately.
              }

              ...

              let window = web_sys::window().expect("should have a window in this context");
              let application_reference = Rc::new(RefCell::new(MyGame::new()));

              let request_animation_frame = { // request_animation_frame is not forgotten! Its ownership is moved into the timer callback.
              let application_reference = application_reference.clone();
              let request_animation_frame_callback = Closure::wrap(Box::new(move || {
              let mut application = application_reference.borrow_mut();
              application.should_request_render = true;
              application.handle_animation_frame(); // handle_animation_frame being your main loop.
              }) as Box<FnMut()>);
              let window = window.clone();
              move || {
              window
              .request_animation_frame(
              request_animation_frame_callback.as_ref().unchecked_ref(),
              )
              .unwrap();
              }
              };
              request_animation_frame(); // fire the first request immediately

              let timer_closure = Closure::wrap(
              Box::new(move || { // move both request_animation_frame and application_reference here.
              let mut application = application_reference.borrow_mut();
              if application.should_request_render {
              application.should_request_render = false;
              request_animation_frame();
              }
              }) as Box<FnMut()>
              );
              window.set_interval_with_callback_and_timeout_and_arguments_0(
              timer_closure.as_ref().unchecked_ref(),
              25, // minimum ms per frame
              )?;
              timer_closure.forget(); // this leaks it, you could store it somewhere or whatever, depends if it's guaranteed to live as long as the page


              You can store the result of set_interval and the timer_closure in Options in your game state so that your game can clean itself up if needed for some reason (maybe? I haven't tried this, and it would seem to cause a free of self?). The circular reference won't erase itself unless broken (you're then storing Rcs to the application inside the application effectively). It should also enable you to change the max fps while running, by stopping the interval and creating another using the same closure.






              share|improve this answer


























                1












                1








                1






                I'm a Rust novice, but here's how I addressed the same issue.



                You can eliminate the problematic window.request_animation_frame recursion and implement an FPS cap at the same time by invoking window.request_animation_frame from a window.set_interval callback which checks a Rc<RefCell<bool>> or something to see if there's an animation frame request still pending. I'm not sure if the inactive tab behavior will be any different in practice.



                I put the bool into my application state since I'm using an Rc<RefCell<...>> to that anyway for other event handling. I haven't checked that this below compiles as is, but here's the relevant parts of how I'm doing this:



                pub struct MyGame {
                ...
                should_request_render: bool, // Don't request another render until the previous runs, init to false since we'll fire the first one immediately.
                }

                ...

                let window = web_sys::window().expect("should have a window in this context");
                let application_reference = Rc::new(RefCell::new(MyGame::new()));

                let request_animation_frame = { // request_animation_frame is not forgotten! Its ownership is moved into the timer callback.
                let application_reference = application_reference.clone();
                let request_animation_frame_callback = Closure::wrap(Box::new(move || {
                let mut application = application_reference.borrow_mut();
                application.should_request_render = true;
                application.handle_animation_frame(); // handle_animation_frame being your main loop.
                }) as Box<FnMut()>);
                let window = window.clone();
                move || {
                window
                .request_animation_frame(
                request_animation_frame_callback.as_ref().unchecked_ref(),
                )
                .unwrap();
                }
                };
                request_animation_frame(); // fire the first request immediately

                let timer_closure = Closure::wrap(
                Box::new(move || { // move both request_animation_frame and application_reference here.
                let mut application = application_reference.borrow_mut();
                if application.should_request_render {
                application.should_request_render = false;
                request_animation_frame();
                }
                }) as Box<FnMut()>
                );
                window.set_interval_with_callback_and_timeout_and_arguments_0(
                timer_closure.as_ref().unchecked_ref(),
                25, // minimum ms per frame
                )?;
                timer_closure.forget(); // this leaks it, you could store it somewhere or whatever, depends if it's guaranteed to live as long as the page


                You can store the result of set_interval and the timer_closure in Options in your game state so that your game can clean itself up if needed for some reason (maybe? I haven't tried this, and it would seem to cause a free of self?). The circular reference won't erase itself unless broken (you're then storing Rcs to the application inside the application effectively). It should also enable you to change the max fps while running, by stopping the interval and creating another using the same closure.






                share|improve this answer














                I'm a Rust novice, but here's how I addressed the same issue.



                You can eliminate the problematic window.request_animation_frame recursion and implement an FPS cap at the same time by invoking window.request_animation_frame from a window.set_interval callback which checks a Rc<RefCell<bool>> or something to see if there's an animation frame request still pending. I'm not sure if the inactive tab behavior will be any different in practice.



                I put the bool into my application state since I'm using an Rc<RefCell<...>> to that anyway for other event handling. I haven't checked that this below compiles as is, but here's the relevant parts of how I'm doing this:



                pub struct MyGame {
                ...
                should_request_render: bool, // Don't request another render until the previous runs, init to false since we'll fire the first one immediately.
                }

                ...

                let window = web_sys::window().expect("should have a window in this context");
                let application_reference = Rc::new(RefCell::new(MyGame::new()));

                let request_animation_frame = { // request_animation_frame is not forgotten! Its ownership is moved into the timer callback.
                let application_reference = application_reference.clone();
                let request_animation_frame_callback = Closure::wrap(Box::new(move || {
                let mut application = application_reference.borrow_mut();
                application.should_request_render = true;
                application.handle_animation_frame(); // handle_animation_frame being your main loop.
                }) as Box<FnMut()>);
                let window = window.clone();
                move || {
                window
                .request_animation_frame(
                request_animation_frame_callback.as_ref().unchecked_ref(),
                )
                .unwrap();
                }
                };
                request_animation_frame(); // fire the first request immediately

                let timer_closure = Closure::wrap(
                Box::new(move || { // move both request_animation_frame and application_reference here.
                let mut application = application_reference.borrow_mut();
                if application.should_request_render {
                application.should_request_render = false;
                request_animation_frame();
                }
                }) as Box<FnMut()>
                );
                window.set_interval_with_callback_and_timeout_and_arguments_0(
                timer_closure.as_ref().unchecked_ref(),
                25, // minimum ms per frame
                )?;
                timer_closure.forget(); // this leaks it, you could store it somewhere or whatever, depends if it's guaranteed to live as long as the page


                You can store the result of set_interval and the timer_closure in Options in your game state so that your game can clean itself up if needed for some reason (maybe? I haven't tried this, and it would seem to cause a free of self?). The circular reference won't erase itself unless broken (you're then storing Rcs to the application inside the application effectively). It should also enable you to change the max fps while running, by stopping the interval and creating another using the same closure.







                share|improve this answer














                share|improve this answer



                share|improve this answer








                edited Nov 19 '18 at 18:46

























                answered Nov 19 '18 at 18:39









                djozisdjozis

                93




                93






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.





                    Some of your past answers have not been well-received, and you're in danger of being blocked from answering.


                    Please pay close attention to the following guidance:


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53000413%2fhow-can-i-work-around-not-being-able-to-export-functions-with-lifetimes-when-usi%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    MongoDB - Not Authorized To Execute Command

                    How to fix TextFormField cause rebuild widget in Flutter

                    in spring boot 2.1 many test slices are not allowed anymore due to multiple @BootstrapWith