Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend memory management chapter #1458

Closed
Hofer-Julian opened this issue Aug 9, 2023 · 0 comments · Fixed by #1459
Closed

Extend memory management chapter #1458

Hofer-Julian opened this issue Aug 9, 2023 · 0 comments · Fixed by #1459
Assignees
Labels

Comments

@Hofer-Julian
Copy link
Collaborator

The memory management chapter already covers many foot guns when it comes to memory management with gtk-rs, but I think we could start with an even simpler example:

use gtk::prelude::*;
use gtk::{self, glib, Application, ApplicationWindow, Button, Orientation};

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();

    // A mutable integer
    let mut number = 0;

    // Connect callbacks
    // When a button is clicked, `number` should be changed
    button_increase.connect_clicked(|_| number += 1);

    // Add buttons to `gtk_box`
    let gtk_box = gtk::Box::builder()
        .orientation(Orientation::Vertical)
        .build();
    gtk_box.append(&button_increase);

    // Create a window
    let window = ApplicationWindow::builder()
        .application(application)
        .title("My GTK App")
        .child(&gtk_box)
        .build();

    // Present the window
    window.present();
}

Fails with:

error[E0594]: cannot assign to `number`, as it is a captured variable in a `Fn` closure
  --> g_object_memory_management/1/main.rs:32:41
   |
32 |     button_increase.connect_clicked(|_| number += 1);
   |                                         ^^^^^^^^^^^ cannot assign

error[E0373]: closure may outlive the current function, but it borrows `number`, which is owned by the current function
  --> g_object_memory_management/1/main.rs:32:37
   |
32 |     button_increase.connect_clicked(|_| number += 1);
   |                                     ^^^ ------ `number` is borrowed here
   |                                     |
   |                                     may outlive borrowed value `number`
   |
note: function requires argument type to outlive `'static`
  --> g_object_memory_management/1/main.rs:32:5
   |
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
   |
32 |     button_increase.connect_clicked(move |_| number += 1);
   |                                     ++++

The move is already covered, but we should also mention that signal handlers are Fn not FnMut.
This means they cannot mutate state.
@sdroege says they are Fn because:

  • they can be called from inside themselves
  • for multi-threaded objects, they can be called multiple times concurrently from different threads

After explaining the requirement of static' lifetimes and inner mutability, we can move on to shared state with Rc and GObjects.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant