diff --git a/book/listings/Cargo.toml b/book/listings/Cargo.toml
index a34202f60627..593e0ed086bb 100644
--- a/book/listings/Cargo.toml
+++ b/book/listings/Cargo.toml
@@ -102,6 +102,10 @@ path = "g_object_memory_management/3/main.rs"
name = "g_object_memory_management_4"
path = "g_object_memory_management/4/main.rs"
+[[bin]]
+name = "g_object_memory_management_5"
+path = "g_object_memory_management/5/main.rs"
+
# g_object_properties
[[bin]]
name = "g_object_properties_1"
diff --git a/book/listings/g_object_memory_management/1/main.rs b/book/listings/g_object_memory_management/1/main.rs
index 44262976ee9d..f4d987ef412b 100644
--- a/book/listings/g_object_memory_management/1/main.rs
+++ b/book/listings/g_object_memory_management/1/main.rs
@@ -1,8 +1,6 @@
-use std::cell::Cell;
-use std::rc::Rc;
-
use gtk::prelude::*;
-use gtk::{self, glib, Application, ApplicationWindow, Button, Orientation};
+use gtk::{glib, Application, ApplicationWindow, Button};
+use std::cell::Cell;
const APP_ID: &str = "org.gtk_rs.GObjectMemoryManagement1";
@@ -16,7 +14,9 @@ fn main() -> glib::ExitCode {
// Run the application
app.run()
}
-fn build_ui(app: &Application) {
+
+// ANCHOR: build_ui
+fn build_ui(application: &Application) {
// Create two buttons
let button_increase = Button::builder()
.label("Increase")
@@ -25,38 +25,22 @@ fn build_ui(app: &Application) {
.margin_start(12)
.margin_end(12)
.build();
- let button_decrease = Button::builder()
- .label("Decrease")
- .margin_top(12)
- .margin_bottom(12)
- .margin_start(12)
- .margin_end(12)
- .build();
- // ANCHOR: callback
- // Reference-counted object with inner-mutability
- let number = Rc::new(Cell::new(0));
+ // A mutable integer
+ let number = Cell::new(0);
- // Connect callbacks, when a button is clicked `number` will be changed
- let number_copy = number.clone();
- button_increase.connect_clicked(move |_| number_copy.set(number_copy.get() + 1));
- button_decrease.connect_clicked(move |_| number.set(number.get() - 1));
- // ANCHOR_END: callback
-
- // Add buttons to `gtk_box`
- let gtk_box = gtk::Box::builder()
- .orientation(Orientation::Vertical)
- .build();
- gtk_box.append(&button_increase);
- gtk_box.append(&button_decrease);
+ // Connect callbacks
+ // When a button is clicked, `number` should be changed
+ button_increase.connect_clicked(move |_| number.set(number.get() + 1));
// Create a window
let window = ApplicationWindow::builder()
- .application(app)
+ .application(application)
.title("My GTK App")
- .child(>k_box)
+ .child(&button_increase)
.build();
// Present the window
window.present();
}
+// ANCHOR_END: build_ui
diff --git a/book/listings/g_object_memory_management/2/main.rs b/book/listings/g_object_memory_management/2/main.rs
index 35d335c87101..da82075af8aa 100644
--- a/book/listings/g_object_memory_management/2/main.rs
+++ b/book/listings/g_object_memory_management/2/main.rs
@@ -1,7 +1,6 @@
use std::cell::Cell;
use std::rc::Rc;
-use glib::clone;
use gtk::prelude::*;
use gtk::{self, glib, Application, ApplicationWindow, Button, Orientation};
@@ -34,17 +33,14 @@ fn build_ui(app: &Application) {
.margin_end(12)
.build();
- // Reference-counted object with inner mutability
- let number = Rc::new(Cell::new(0));
- // Connect callbacks
- // When a button is clicked, `number` will be changed
// ANCHOR: callback
- button_increase.connect_clicked(clone!(@strong number => move |_| {
- number.set(number.get() + 1);
- }));
- button_decrease.connect_clicked(move |_| {
- number.set(number.get() - 1);
- });
+ // Reference-counted object with inner-mutability
+ let number = Rc::new(Cell::new(0));
+
+ // Connect callbacks, when a button is clicked `number` will be changed
+ let number_copy = number.clone();
+ button_increase.connect_clicked(move |_| number_copy.set(number_copy.get() + 1));
+ button_decrease.connect_clicked(move |_| number.set(number.get() - 1));
// ANCHOR_END: callback
// Add buttons to `gtk_box`
diff --git a/book/listings/g_object_memory_management/3/main.rs b/book/listings/g_object_memory_management/3/main.rs
index 507ebd26bcc2..52d68b7652ff 100644
--- a/book/listings/g_object_memory_management/3/main.rs
+++ b/book/listings/g_object_memory_management/3/main.rs
@@ -17,7 +17,6 @@ fn main() -> glib::ExitCode {
// Run the application
app.run()
}
-
fn build_ui(app: &Application) {
// Create two buttons
let button_increase = Button::builder()
@@ -35,21 +34,17 @@ fn build_ui(app: &Application) {
.margin_end(12)
.build();
+ // Reference-counted object with inner mutability
let number = Rc::new(Cell::new(0));
-
- // ANCHOR: callback
// Connect callbacks
- // When a button is clicked, `number` and label of the other button will be changed
- button_increase.connect_clicked(clone!(@weak number, @strong button_decrease =>
- move |_| {
- number.set(number.get() + 1);
- button_decrease.set_label(&number.get().to_string());
- }));
- button_decrease.connect_clicked(clone!(@strong button_increase =>
- move |_| {
- number.set(number.get() - 1);
- button_increase.set_label(&number.get().to_string());
+ // When a button is clicked, `number` will be changed
+ // ANCHOR: callback
+ button_increase.connect_clicked(clone!(@strong number => move |_| {
+ number.set(number.get() + 1);
}));
+ button_decrease.connect_clicked(move |_| {
+ number.set(number.get() - 1);
+ });
// ANCHOR_END: callback
// Add buttons to `gtk_box`
diff --git a/book/listings/g_object_memory_management/4/main.rs b/book/listings/g_object_memory_management/4/main.rs
index f9aa0e71fe7a..2e09ff0a4223 100644
--- a/book/listings/g_object_memory_management/4/main.rs
+++ b/book/listings/g_object_memory_management/4/main.rs
@@ -35,41 +35,36 @@ fn build_ui(app: &Application) {
.margin_end(12)
.build();
- // Reference-counted object with inner mutability
let number = Rc::new(Cell::new(0));
// ANCHOR: callback
// Connect callbacks
// When a button is clicked, `number` and label of the other button will be changed
- button_increase.connect_clicked(clone!(@weak number, @weak button_decrease =>
+ button_increase.connect_clicked(clone!(@weak number, @strong button_decrease =>
move |_| {
number.set(number.get() + 1);
button_decrease.set_label(&number.get().to_string());
}));
- button_decrease.connect_clicked(clone!(@weak button_increase =>
+ button_decrease.connect_clicked(clone!(@strong button_increase =>
move |_| {
number.set(number.get() - 1);
button_increase.set_label(&number.get().to_string());
}));
// ANCHOR_END: callback
- // ANCHOR: box_append
// Add buttons to `gtk_box`
let gtk_box = gtk::Box::builder()
.orientation(Orientation::Vertical)
.build();
gtk_box.append(&button_increase);
gtk_box.append(&button_decrease);
- // ANCHOR_END: box_append
- // ANCHOR: window_child
// Create a window
let window = ApplicationWindow::builder()
.application(app)
.title("My GTK App")
.child(>k_box)
.build();
- // ANCHOR_END: window_child
// Present the window
window.present();
diff --git a/book/listings/g_object_memory_management/5/main.rs b/book/listings/g_object_memory_management/5/main.rs
new file mode 100644
index 000000000000..d9553e78562b
--- /dev/null
+++ b/book/listings/g_object_memory_management/5/main.rs
@@ -0,0 +1,76 @@
+use std::cell::Cell;
+use std::rc::Rc;
+
+use glib::clone;
+use gtk::prelude::*;
+use gtk::{self, glib, Application, ApplicationWindow, Button, Orientation};
+
+const APP_ID: &str = "org.gtk_rs.GObjectMemoryManagement5";
+
+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);
+
+ // Run the application
+ app.run()
+}
+
+fn build_ui(app: &Application) {
+ // Create two buttons
+ let button_increase = Button::builder()
+ .label("Increase")
+ .margin_top(12)
+ .margin_bottom(12)
+ .margin_start(12)
+ .margin_end(12)
+ .build();
+ let button_decrease = Button::builder()
+ .label("Decrease")
+ .margin_top(12)
+ .margin_bottom(12)
+ .margin_start(12)
+ .margin_end(12)
+ .build();
+
+ // Reference-counted object with inner mutability
+ let number = Rc::new(Cell::new(0));
+
+ // ANCHOR: callback
+ // Connect callbacks
+ // When a button is clicked, `number` and label of the other button will be changed
+ button_increase.connect_clicked(clone!(@weak number, @weak button_decrease =>
+ move |_| {
+ number.set(number.get() + 1);
+ button_decrease.set_label(&number.get().to_string());
+ }));
+ button_decrease.connect_clicked(clone!(@weak button_increase =>
+ move |_| {
+ number.set(number.get() - 1);
+ button_increase.set_label(&number.get().to_string());
+ }));
+ // ANCHOR_END: callback
+
+ // ANCHOR: box_append
+ // Add buttons to `gtk_box`
+ let gtk_box = gtk::Box::builder()
+ .orientation(Orientation::Vertical)
+ .build();
+ gtk_box.append(&button_increase);
+ gtk_box.append(&button_decrease);
+ // ANCHOR_END: box_append
+
+ // ANCHOR: window_child
+ // Create a window
+ let window = ApplicationWindow::builder()
+ .application(app)
+ .title("My GTK App")
+ .child(>k_box)
+ .build();
+ // ANCHOR_END: window_child
+
+ // Present the window
+ window.present();
+}
diff --git a/book/src/g_object_memory_management.md b/book/src/g_object_memory_management.md
index 6e7e62d92643..0819019163fb 100644
--- a/book/src/g_object_memory_management.md
+++ b/book/src/g_object_memory_management.md
@@ -1,14 +1,18 @@
# Memory Management
-A GObject (or [`glib::Object`](https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib/object/struct.Object.html) in Rust terms) is a reference-counted, mutable object.
-Let's see in a set of real life examples which consequences this has.
+Memory management when writing a gtk-rs app can be a bit tricky.
+Let's have a look why that is the case and how to deal with that.
+
+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, Orientation};
+#use gtk::{self, glib, Application, ApplicationWindow, Button};
#
#const APP_ID: &str = "org.gtk_rs.GObjectMemoryManagement0";
#
+// DOES NOT COMPILE!
fn main() -> glib::ExitCode {
// Create a new application
let app = Application::builder().application_id(APP_ID).build();
@@ -29,13 +33,6 @@ fn build_ui(application: &Application) {
.margin_start(12)
.margin_end(12)
.build();
- let button_decrease = Button::builder()
- .label("Decrease")
- .margin_top(12)
- .margin_bottom(12)
- .margin_start(12)
- .margin_end(12)
- .build();
// A mutable integer
let mut number = 0;
@@ -43,20 +40,12 @@ fn build_ui(application: &Application) {
// Connect callbacks
// When a button is clicked, `number` should be changed
button_increase.connect_clicked(|_| number += 1);
- button_decrease.connect_clicked(|_| number -= 1);
-
- // Add buttons to `gtk_box`
- let gtk_box = gtk::Box::builder()
- .orientation(Orientation::Vertical)
- .build();
- gtk_box.append(&button_increase);
- gtk_box.append(&button_decrease);
// Create a window
let window = ApplicationWindow::builder()
.application(application)
.title("My GTK App")
- .child(>k_box)
+ .child(&button_increase)
.build();
// Present the window
@@ -64,75 +53,122 @@ fn build_ui(application: &Application) {
}
```
-Here we would like to create a simple app with two buttons.
-If we click on one button, an integer number should be increased. If we press the other one, it should be decreased.
-The Rust compiler refuses to compile it though.
-
-For once the borrow checker kicked in:
-
-```console
-error[E0499]: cannot borrow `number` as mutable more than once at a time
- --> main.rs:27:37
- |
-26 | button_increase.connect_clicked(|_| number += 1);
- | ------------------------------------------------
- | | | |
- | | | first borrow occurs due to use of `number` in closure
- | | first mutable borrow occurs here
- | argument requires that `number` is borrowed for `'static`
-27 | button_decrease.connect_clicked(|_| number -= 1);
- | ^^^ ------ second borrow occurs due to use of `number` in closure
- | |
- | second mutable borrow occurs here
-```
-
-Also, the compiler tells us that our closures may outlive `number`:
+The Rust compiler refuses to compile this application while spitting our multiple error messages.
+Let's have a look at them one by one.
```console
error[E0373]: closure may outlive the current function, but it borrows `number`, which is owned by the current function
- --> main.rs:26:37
|
-26 | button_increase.connect_clicked(|_| number += 1);
+32 | button_increase.connect_clicked(|_| number += 1);
| ^^^ ------ `number` is borrowed here
| |
| may outlive borrowed value `number`
|
note: function requires argument type to outlive `'static`
- --> main.rs:26:5
|
-26 | button_increase.connect_clicked(|_| number += 1);
+32 | button_increase.connect_clicked(|_| number += 1);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
help: to force the closure to take ownership of `number` (and any other referenced variables), use the `move` keyword
|
-26 | button_increase.connect_clicked(move |_| number += 1);
- | ^^^^^^^^
+32 | button_increase.connect_clicked(move |_| number += 1);
+ |
```
-Thinking about the second error message, it makes sense that the closure requires the lifetimes of references to be `'static`.
-The compiler cannot know when the user presses a button, so references must live forever.
-And our `number` gets immediately deallocated after it reaches the end of its scope.
-The error message is also suggesting that we could take ownership of `number`.
-But is there actually a way that both closures could take ownership of the same value?
+Our closure only borrows `number`.
+Signal handlers in GTK require `static'` lifetimes for their references, so we cannot borrow a variable that only lives for the scope of the function `build_ui`.
+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.
-Yes! That is exactly what the [`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) type is there for.
-The `Rc` counts the number of strong references created via [`Clone::clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html#tymethod.clone) and released via [`Drop::drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html#tymethod.drop), and only deallocates it when this number drops to zero.
-We call every object containing a strong reference a shared owner of the value.
-If we want to modify the content of our [`Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html),
-we can use the [`Cell`](https://doc.rust-lang.org/std/cell/struct.Cell.html) type.
+```rust ,no_run,compile_fail
+#use gtk::prelude::*;
+#use gtk::{self, glib, Application, ApplicationWindow, Button};
+#
+#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();
+#
+# // Connect to "activate" signal of `app`
+# app.connect_activate(build_ui);
+#
+# // 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();
+#
+ // DOES NOT COMPILE!
+ // A mutable integer
+ let mut number = 0;
+
+ // Connect callbacks
+ // When a button is clicked, `number` should be changed
+ button_increase.connect_clicked(move |_| number += 1);
+#
+# // Create a window
+# let window = ApplicationWindow::builder()
+# .application(application)
+# .title("My GTK App")
+# .child(&button_increase)
+# .build();
+#
+# // Present the window
+# window.present();
+#}
+```
+
+This still leaves the following error message:
+
+```console
+
+error[E0594]: cannot assign to `number`, as it is a captured variable in a `Fn` closure
+ |
+32 | button_increase.connect_clicked(move |_| number += 1);
+ | ^^^^^^^^^^^ cannot assign
+```
+
+In order to understand that error message we have to understand the difference between the three closure traits `FnOnce`, `FnMut` and `Fn`.
+APIs that take closures implementing the `FnOnce` trait give the most freedom to the API consumer.
+The closure is called only once, so it can even consume its state.
+Signal handlers can be called multiple times, so they cannot accept `FnOnce`.
+
+The more restrictive `FnMut` trait doesn't allow closures to consume their state, but they can still mutate it.
+Signal handlers can't allow this either, because they can be called from inside themselves.
+This would lead to multiple mutable references which the borrow checker doesn't appreciate at all.
+
+This leaves `Fn`.
+State can be immutably borrowed, but then how can we modify `number`?
+We need a data type with interior mutability like [`std::cell::Cell`](https://doc.rust-lang.org/std/cell/struct.Cell.html).
> The `Cell` class is only suitable for objects that implement the [`Copy`](https://doc.rust-lang.org/core/marker/trait.Copy.html) trait.
> For other objects, [`RefCell`](https://doc.rust-lang.org/std/cell/struct.RefCell.html) is the way to go.
> You can learn more about interior mutability in this [section](https://marabos.nl/atomics/basics.html#interior-mutability) of the book _Rust Atomics and Locks_.
-Filename: listings/g_object_memory_management/1/main.rs
+Filename: listings/g_object_memory_management/1/main.rs
```rust ,no_run,noplayground
-{{#rustdoc_include ../listings/g_object_memory_management/1/main.rs:callback}}
+{{#rustdoc_include ../listings/g_object_memory_management/1/main.rs:build_ui}}
```
-It is not very nice though to fill the scope with temporary variables like `number_copy`.
-We can improve that by using the [`glib::clone!`](https://gtk-rs.org/gtk-rs-core/stable/latest/docs/glib/macro.clone.html) macro.
+This now compiles as expected.
+Let's try a slightly more complicated example: two buttons which both modify the same `number`.
+For that, we need a way that both closures take ownership of the same value?
+
+That is exactly what the [`std::rc::Rc`](https://doc.rust-lang.org/std/rc/struct.Rc.html) type is there for.
+`Rc` counts the number of strong references created via [`Clone::clone`](https://doc.rust-lang.org/std/clone/trait.Clone.html#tymethod.clone) and released via [`Drop::drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html#tymethod.drop), and only deallocates the value when this number drops to zero.
+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
@@ -140,8 +176,8 @@ Filename: listings/g_object_memory_management/3/main.rs
@@ -149,6 +185,15 @@ Filename: listings/g_object_memory_management/4/main.rs
+
+```rust ,no_run,noplayground
+{{#rustdoc_include ../listings/g_object_memory_management/4/main.rs:callback}}
+```
+
If we now click on one button, the other button's label gets changed.
But whoops!
@@ -160,10 +205,10 @@ If this chain leads to a circle, none of the values in this cycle ever get deall
With weak references we can break this cycle, because they don't keep their value alive but instead provide a way to retrieve a strong reference if the value is still alive.
Since we want our apps to free unneeded memory, we should use weak references for the buttons instead.
-Filename: listings/g_object_memory_management/4/main.rs
+Filename: listings/g_object_memory_management/5/main.rs
```rust ,no_run,noplayground
-{{#rustdoc_include ../listings/g_object_memory_management/4/main.rs:callback}}
+{{#rustdoc_include ../listings/g_object_memory_management/5/main.rs:callback}}
```
The reference cycle is broken.
@@ -177,18 +222,18 @@ If we had moved weak references in both closures, nothing would have kept `numbe
Thinking about this, `button_increase` and `button_decrease` are also dropped at the end of the scope of `build_ui`.
Who then keeps the buttons alive?
-Filename: listings/g_object_memory_management/4/main.rs
+Filename: listings/g_object_memory_management/5/main.rs
```rust ,no_run,noplayground
-{{#rustdoc_include ../listings/g_object_memory_management/4/main.rs:box_append}}
+{{#rustdoc_include ../listings/g_object_memory_management/5/main.rs:box_append}}
```
When we append the buttons to the `gtk_box`, `gtk_box` keeps a strong reference to them.
-Filename: listings/g_object_memory_management/4/main.rs
+Filename: listings/g_object_memory_management/5/main.rs
```rust ,no_run,noplayground
-{{#rustdoc_include ../listings/g_object_memory_management/4/main.rs:window_child}}
+{{#rustdoc_include ../listings/g_object_memory_management/5/main.rs:window_child}}
```
When we set `gtk_box` as child of `window`, `window` keeps a strong reference to it.
diff --git a/book/src/main_event_loop.md b/book/src/main_event_loop.md
index 21af76bc72c7..08c3090cdb80 100644
--- a/book/src/main_event_loop.md
+++ b/book/src/main_event_loop.md
@@ -136,8 +136,7 @@ But why did we not do the same thing with our multi-threaded example?
# .margin_end(12)
# .build();
#
- // DOES NOT COMPILE
-
+ // DOES NOT COMPILE!
// Connect to "clicked" signal of `button`
button.connect_clicked(move |button| {
button.clone();