Skip to content

Commit 5c58976

Browse files
committed
fix: convert callback lifetimes to 'static
Except for ev.on_tick which has a test against use-after-free in compile_tests.rs.
1 parent c415fe3 commit 5c58976

File tree

6 files changed

+56
-36
lines changed

6 files changed

+56
-36
lines changed

CONTRIBUTING/callbacks.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Their intended use is for `to_heap_ptr` to turn a Rust function into a bag of bi
1515
by "generic locking" the user and wrapper functions, like so:
1616

1717
```rust
18-
fn on_whatever<'ctx, F: FnMut(&Whatever)>(&mut self, _ctx: &'ctx UI, callback: F) {
18+
fn on_whatever<'ctx, F: FnMut(&Whatever) + 'static>(&mut self, _ctx: &'ctx UI, callback: F) {
1919

2020
fn c_callback<G: FnMut(&Whatever)> { /* ... do stuff ... */ }
2121

@@ -26,5 +26,5 @@ fn on_whatever<'ctx, F: FnMut(&Whatever)>(&mut self, _ctx: &'ctx UI, callback: F
2626
This is somewhat verbose but ensures that the types do not deviate, which would be unsafe.
2727

2828
Callbacks should be named `on_event` where `event` is, for instance, `clicked` or
29-
`closing`.
29+
`closing`. The functions taken by callbacks must always have the `'static` bound.
3030

iui/src/compile_tests.rs

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,21 @@
11
//! Examples of unsound code that IUI statically prevents from compiling.
22
//!
3-
//! Here, we attempt to place use-after-free some callbacks.
3+
//! Here, we attempt to use-after-free some callbacks.
44
//!
55
//! ```compile_fail
6-
//! let ev = iui::UI::init().unwrap();
6+
//! let ui = iui::UI::init().unwrap();
77
//!
88
//! {
99
//! let v = vec![1, 2, 3, 4];
10-
//! ev.queue_main(|| {
10+
//! ui.queue_main(|| {
1111
//! for i in &v {
1212
//! println!("{}", i);
1313
//! }
1414
//! });
1515
//! }
16-
//!
17-
//! ev.quit();
18-
//! ev.main();
19-
//! ```
20-
//!
21-
//! ```compile_fail
22-
//! let ev = iui::UI::init().unwrap();
23-
//!
24-
//! {
25-
//! let v = vec![1, 2, 3, 4];
26-
//! ev.on_should_quit(|| {
27-
//! for i in &v {
28-
//! println!("{}", i);
29-
//! }
30-
//! });
31-
//! }
32-
//!
33-
//! ev.quit();
34-
//! ev.main();
3516
//! ```
3617
//!
18+
//! This one is OK, because it moves the `Vec` into the closure's scope.
3719
//! ```
3820
//! let ev = iui::UI::init().unwrap();
3921
//!
@@ -47,3 +29,41 @@
4729
//! ev.quit();
4830
//! ev.main();
4931
//! ```
32+
//!
33+
//! This one tries to use a reference to a string that is dropped out of scope.
34+
//! ```compile_fail
35+
//! # use iui::prelude::*;
36+
//! # use iui::controls::{Button};
37+
//! let ui = UI::init().unwrap();
38+
//! let mut win = Window::new(&ui, "Test App", 200, 200, WindowType::NoMenubar);
39+
//! let mut button = Button::new(&ui, "Button");
40+
//!
41+
//! {
42+
//! let s = String::from("Whatever!");
43+
//! let callback = |b: &mut Button| { println!("{}", s)};
44+
//! button.on_clicked(&ui, callback);
45+
//! }
46+
//!
47+
//! win.set_child(&ui, button);
48+
//! ```
49+
//!
50+
//! Here we try to use-after-free data in the on-tick callback.
51+
//!
52+
//! ```compile_fail
53+
//! # use iui::prelude::*;
54+
//! # use iui::controls::{Button};
55+
//! let ui = UI::init().unwrap();
56+
//! let mut ev = ui.event_loop();
57+
//! let win = Window::new(&ui, "Test App", 200, 200, WindowType::NoMenubar);
58+
//!
59+
//! {
60+
//! let s = String::from("Whatever!");
61+
//! let callback = || { println!("{}", s) };
62+
//! ev.on_tick(&ui, callback);
63+
//! }
64+
//!
65+
//! ev.next_tick(&ui);
66+
//! ui.quit();
67+
//! ev.next_tick(&ui);
68+
//!
69+
//! ```

iui/src/controls/button.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ impl Button {
4646
/// Run the given callback when the button is clicked.
4747
pub fn on_clicked<'ctx, F>(&mut self, _ctx: &'ctx UI, callback: F)
4848
where
49-
F: FnMut(&mut Button) + 'ctx,
49+
F: FnMut(&mut Button) + 'static,
5050
{
5151
extern "C" fn c_callback<G>(button: *mut uiButton, data: *mut c_void)
5252
where

iui/src/controls/entry.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,13 @@ use str_tools::{from_toolkit_string, to_toolkit_string};
1919
pub trait NumericEntry {
2020
fn value(&self, ctx: &UI) -> i32;
2121
fn set_value(&mut self, ctx: &UI, value: i32);
22-
fn on_changed<'ctx, F: FnMut(i32) + 'ctx>(&mut self, ctx: &'ctx UI, callback: F);
22+
fn on_changed<'ctx, F: FnMut(i32) + 'static>(&mut self, ctx: &'ctx UI, callback: F);
2323
}
2424

2525
pub trait TextEntry {
2626
fn value(&self, ctx: &UI) -> String;
2727
fn set_value(&mut self, ctx: &UI, value: &str);
28-
fn on_changed<'ctx, F: FnMut(String) + 'ctx>(&mut self, ctx: &'ctx UI, callback: F);
28+
fn on_changed<'ctx, F: FnMut(String) + 'static>(&mut self, ctx: &'ctx UI, callback: F);
2929
}
3030

3131
define_control! {
@@ -70,7 +70,7 @@ impl NumericEntry for Spinbox {
7070

7171
fn on_changed<'ctx, F>(&mut self, _ctx: &'ctx UI, callback: F)
7272
where
73-
F: FnMut(i32) + 'ctx,
73+
F: FnMut(i32) + 'static
7474
{
7575
extern "C" fn c_callback<G>(spinbox: *mut uiSpinbox, data: *mut c_void)
7676
where
@@ -103,7 +103,7 @@ impl NumericEntry for Slider {
103103

104104
fn on_changed<'ctx, F>(&mut self, _ctx: &'ctx UI, callback: F)
105105
where
106-
F: FnMut(i32) + 'ctx,
106+
F: FnMut(i32) + 'static
107107
{
108108
extern "C" fn c_callback<G>(slider: *mut uiSlider, data: *mut c_void)
109109
where
@@ -169,7 +169,7 @@ impl TextEntry for Entry {
169169

170170
fn on_changed<'ctx, F>(&mut self, _ctx: &'ctx UI, callback: F)
171171
where
172-
F: FnMut(String) + 'ctx,
172+
F: FnMut(String) + 'static,
173173
{
174174
extern "C" fn c_callback<G>(entry: *mut uiEntry, data: *mut c_void)
175175
where
@@ -235,7 +235,7 @@ impl TextEntry for MultilineEntry {
235235

236236
fn on_changed<'ctx, F>(&mut self, _ctx: &'ctx UI, callback: F)
237237
where
238-
F: FnMut(String) + 'ctx,
238+
F: FnMut(String) + 'static,
239239
{
240240
extern "C" fn c_callback<G>(entry: *mut uiMultilineEntry, data: *mut c_void)
241241
where
@@ -288,7 +288,7 @@ impl Combobox {
288288

289289
pub fn on_selected<'ctx, F>(&mut self, _ctx: &'ctx UI, callback: F)
290290
where
291-
F: FnMut(i32) + 'ctx,
291+
F: FnMut(i32) + 'static,
292292
{
293293
extern "C" fn c_callback<G>(combobox: *mut uiCombobox, data: *mut c_void)
294294
where
@@ -331,7 +331,7 @@ impl Checkbox {
331331

332332
pub fn on_toggled<'ctx, F>(&mut self, _ctx: &'ctx UI, callback: F)
333333
where
334-
F: FnMut(bool) + 'ctx,
334+
F: FnMut(bool) + 'static,
335335
{
336336
extern "C" fn c_callback<G>(checkbox: *mut uiCheckbox, data: *mut c_void)
337337
where
@@ -376,7 +376,7 @@ impl RadioButtons {
376376
unsafe { ui_sys::uiRadioButtonsSetSelected(self.uiRadioButtons, idx); }
377377
}
378378

379-
pub fn on_selected<'ctx, F: FnMut(i32) + 'ctx>(&self, _ctx: &'ctx UI, callback: F) {
379+
pub fn on_selected<'ctx, F: FnMut(i32) + 'static>(&self, _ctx: &'ctx UI, callback: F) {
380380
unsafe {
381381
let mut data: Box<Box<dyn FnMut(i32)>> = Box::new(Box::new(callback));
382382
ui_sys::uiRadioButtonsOnSelected(

iui/src/controls/window.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ impl Window {
9090
/// the application when the window is closed.
9191
pub fn on_closing<'ctx, F>(&mut self, _ctx: &'ctx UI, callback: F)
9292
where
93-
F: FnMut(&mut Window) + 'ctx,
93+
F: FnMut(&mut Window) + 'static,
9494
{
9595
extern "C" fn c_callback<G>(window: *mut uiWindow, data: *mut c_void) -> i32
9696
where

iui/src/menus.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ impl MenuItem {
4848
/// Sets the function to be executed when the item is clicked/selected.
4949
pub fn on_clicked<'ctx, F>(&self, _ctx: &'ctx UI, callback: F)
5050
where
51-
F: FnMut(&MenuItem, &Window) + 'ctx,
51+
F: FnMut(&MenuItem, &Window) + 'static,
5252
{
5353
extern "C" fn c_callback<G: FnMut(&MenuItem, &Window)>(
5454
menu_item: *mut uiMenuItem,

0 commit comments

Comments
 (0)