diff --git a/book/src/g_object_memory_management.md b/book/src/g_object_memory_management.md index 972626b09252..d4941313868a 100644 --- a/book/src/g_object_memory_management.md +++ b/book/src/g_object_memory_management.md @@ -7,10 +7,10 @@ With our first example, we have window with a single button. Every button click should increment an integer `number` by one. ```rust ,no_run,compile_fail -#use gtk::prelude::*; -#use gtk::{self, glib, Application, ApplicationWindow, Button}; +# use gtk::prelude::*; +# use gtk::{self, glib, Application, ApplicationWindow, Button}; # -#const APP_ID: &str = "org.gtk_rs.GObjectMemoryManagement0"; +# const APP_ID: &str = "org.gtk_rs.GObjectMemoryManagement0"; # // DOES NOT COMPILE! fn main() -> glib::ExitCode { @@ -72,7 +72,7 @@ note: function requires argument type to outlive `'static` help: to force the closure to take ownership of `number` (and any other referenced variables), use the `move` keyword | 32 | button_increase.connect_clicked(move |_| number += 1); - | + | ``` Our closure only borrows `number`. @@ -81,31 +81,31 @@ The compiler also suggests how to fix this. By adding the `move` keyword in front of the closure, `number` will be moved into the closure. ```rust ,no_run,compile_fail -#use gtk::prelude::*; -#use gtk::{self, glib, Application, ApplicationWindow, Button}; +# use gtk::prelude::*; +# use gtk::{self, glib, Application, ApplicationWindow, Button}; # -#const APP_ID: &str = "org.gtk_rs.GObjectMemoryManagement0"; +# const APP_ID: &str = "org.gtk_rs.GObjectMemoryManagement0"; # -#fn main() -> glib::ExitCode { -# // Create a new application -# let app = Application::builder().application_id(APP_ID).build(); +# fn main() -> glib::ExitCode { +# // Create a new application +# let app = Application::builder().application_id(APP_ID).build(); # -# // Connect to "activate" signal of `app` -# app.connect_activate(build_ui); +# // Connect to "activate" signal of `app` +# app.connect_activate(build_ui); # -# // Run the application -# app.run() -#} +# // Run the application +# app.run() +# } # -#fn build_ui(application: &Application) { -# // Create two buttons -# let button_increase = Button::builder() -# .label("Increase") -# .margin_top(12) -# .margin_bottom(12) -# .margin_start(12) -# .margin_end(12) -# .build(); +# fn build_ui(application: &Application) { +# // Create two buttons +# let button_increase = Button::builder() +# .label("Increase") +# .margin_top(12) +# .margin_bottom(12) +# .margin_start(12) +# .margin_end(12) +# .build(); # // DOES NOT COMPILE! // A mutable integer @@ -124,7 +124,7 @@ By adding the `move` keyword in front of the closure, `number` will be moved int # # // Present the window # window.present(); -#} +# } ``` This still leaves the following error message: @@ -169,7 +169,6 @@ That is exactly what the [`std::rc::Rc`](https://doc.rust-lang.org/std/rc/struct If we want to modify the content of our [`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html), we can again use the [`Cell`](https://doc.rust-lang.org/std/cell/struct.Cell.html) type. - Filename: listings/g_object_memory_management/2/main.rs ```rust diff --git a/book/src/main_event_loop.md b/book/src/main_event_loop.md index d2c8b2a8023a..ad2b6113def6 100644 --- a/book/src/main_event_loop.md +++ b/book/src/main_event_loop.md @@ -26,7 +26,6 @@ We can't even move the window. The `sleep` call is an artificial example, but frequently, we want to run a slightly longer operation in one go. -
- ## How to Avoid Blocking the Main Loop In order to avoid blocking the main loop, we can spawn a new task with [`gio::spawn_blocking`](https://gtk-rs.org/gtk-rs-core/stable/latest/docs/gio/fn.spawn_blocking.html) and let the operation run on the thread pool. @@ -56,7 +54,6 @@ This is not necessarily what we want. - > If you come from another language than Rust, you might be uncomfortable with the thought of running tasks in separate threads before even looking at other options. > Luckily, Rust's safety guarantees allow you to stop worrying about the nasty bugs that concurrency tends to bring. @@ -123,12 +120,12 @@ But why did we not do the same thing with our multithreaded example? ```rust ,no_run,compile_fail # use std::{thread, time::Duration}; -# +# # use glib::{clone, MainContext, PRIORITY_DEFAULT}; # use gtk::{glib, gio}; # use gtk::prelude::*; # use gtk::{Application, ApplicationWindow, Button}; -# +# # fn main() { # // Create a new application # let app = Application::builder() @@ -137,13 +134,13 @@ But why did we not do the same thing with our multithreaded example? # # // Connect to "activate" signal # app.connect_activate(build_ui); -# +# # // Get command-line arguments # let args: Vec = args().collect(); # // Run the application # app.run(&args); # } -# +# # // When the application is launched… # fn build_ui(application: &Application) { # // Create a window @@ -151,7 +148,7 @@ But why did we not do the same thing with our multithreaded example? # .application(application) # .title("My GTK App") # .build(); -# +# # // Create a button # let button = Button::builder() # .label("Press me!") @@ -160,7 +157,7 @@ But why did we not do the same thing with our multithreaded example? # .margin_start(12) # .margin_end(12) # .build(); -# +# // DOES NOT COMPILE! // Connect to "clicked" signal of `button` button.connect_clicked(move |button| { @@ -175,7 +172,7 @@ But why did we not do the same thing with our multithreaded example? button.set_sensitive(true); }); }); -# +# # // Add button # window.set_child(Some(&button)); # window.present(); @@ -281,32 +278,32 @@ Doing this will block one of the runtime's threads with the GLib main loop, whic Instead, we bind [`tokio::runtime::Runtime`](https://docs.rs/tokio/latest/tokio/runtime/struct.Runtime.html) to a static variable. ```rust -#use std::sync::OnceLock; +# use std::sync::OnceLock; # -#use glib::clone; -#use gtk::glib; -#use gtk::prelude::*; -#use gtk::{Application, ApplicationWindow, Button}; -#use tokio::runtime::Runtime; +# use glib::clone; +# use gtk::glib; +# use gtk::prelude::*; +# use gtk::{Application, ApplicationWindow, Button}; +# use tokio::runtime::Runtime; # -#const APP_ID: &str = "org.gtk_rs.MainEventLoop0"; +# const APP_ID: &str = "org.gtk_rs.MainEventLoop0"; # // DOES NOT COMPILE! static RUNTIME: Runtime = Runtime::new().expect("Setting up tokio runtime needs to succeed."); # -#fn main() -> glib::ExitCode { -# // Create a new application -# let app = Application::builder().application_id(APP_ID).build(); +# fn main() -> glib::ExitCode { +# // Create a new application +# let app = Application::builder().application_id(APP_ID).build(); # -# // Connect to "activate" signal of `app` -# app.connect_activate(build_ui); +# // Connect to "activate" signal of `app` +# app.connect_activate(build_ui); # -# // Run the application -# app.run() -#} +# // Run the application +# app.run() +# } # -#fn build_ui(app: &Application) { +# fn build_ui(app: &Application) { # // Create a button # let button = Button::builder() # .label("Press me!") @@ -347,7 +344,7 @@ static RUNTIME: Runtime = # # // Present window # window.present(); -#} +# } ``` Unfortunately, this doesn't compile. @@ -362,7 +359,6 @@ consider wrapping this expression in `Lazy::new(|| ...)` from the `once_cell` cr We could follow the advice directly, but the standard library also provides solutions for that. With [`std::sync::OnceLock`](https://doc.rust-lang.org/stable/std/sync/struct.OnceLock.html) we can initialize the static with the const function `OnceLock::new()` and initialize it the first time our function `runtime` is called. - Filename: listings/main_event_loop/9/main.rs ```rust @@ -391,12 +387,12 @@ cargo remove tokio reqwest ashpd How to find out whether you can spawn an `async` task on the `glib` main loop? `glib` should be able to spawn the task when the called functions come from libraries that either: + - come from the `glib` ecosystem, - don't depend on a runtime but only on the `futures` family of crates (`futures-io`, `futures-core` etc), - depend on the `async-std` or `smol` runtimes, or - have cargo features that let them depend on `async-std`/`smol` instead of `tokio`. - ## Conclusion You don't want to block the main thread long enough that it is noticeable by the user. @@ -408,7 +404,7 @@ That means you have to run the task in a separate thread and let it send results If your task is [IO bound](https://en.wikipedia.org/wiki/I/O_bound), the answer depends on the crates at your disposal and the type of work to be done. -- Light I/O work with functions from crates using `glib`, `smol`, `async-std` or the `futures` trait family can be spawned on the main loop. This way, you can often avoid synchronization via channels. +- Light I/O work with functions from crates using `glib`, `smol`, `async-std` or the `futures` trait family can be spawned on the main loop. This way, you can often avoid synchronization via channels. - Heavy I/O work might still benefit from running in a separate thread / an async executor to avoid saturating the main loop. If you are unsure, benchmarking is advised. If the best crate for the job relies on `tokio`, you will have to spawn it with the tokio runtime and communicate via channels.