I'm trying to learn rust with its gtk4 bindings and decided to try ListStore as a model for ListBox. In order to bind the model, function which returns Widget is needed, but i need a Label. Why doesn't Label, which inherits Widget (implements IsA<Widget>), satisfy also Widget type?
Error:
error[E0308]: mismatched types
--> src/main.rs:22:5
|
21 | fn create_widget<'r>(label: &'r Object) -> Widget {
| ------ expected `gtk4::Widget` because of return type
22 | Label::new(Some(label.property_value("name").get().unwrap()))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `gtk4::Widget`, found struct `gtk4::Label`
Full code:
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Paned, Label, Orientation, ListBox, ListBoxRow, Widget};
use gtk::gio::ListStore;
use gtk::glib::Type;
use gtk::glib::Object;
fn main() {
println!("Hello, world!");// Create a new application
let app = Application::builder()
.application_id("org.gtk-rs.example")
.build();
// Connect to "activate" signal of `app`
app.connect_activate(build_ui);
// Run the application
app.run();
}
fn create_widget<'r>(label: &'r Object) -> Widget {
Label::new(Some(label.property_value("name").get().unwrap()))
}
fn build_ui(app: &Application) {
let listbox_left = ListBox::new();
let listbox_right = ListBox::new();
let list_model = ListStore::new(Type::STRING);
listbox_right.bind_model(Some(&list_model), create_widget);
let paned = Paned::builder()
.start_child(&listbox_left)
.end_child(&listbox_right)
.orientation(Orientation::Horizontal)
.build();
// Create a window
let window = ApplicationWindow::builder()
.application(app)
.title("My GTK App")
.child(&paned)
.build();
for n in 1..11 {
let n_as_string =(n as u32).to_string();
let mut my = String::from("Left:");
my.push_str(&n_as_string);
let m = Object::with_values(Type::STRING, &[("name", my.to_value())]);
list_model.append(&m.unwrap());
}
// Present window
window.present();
}
Inheritance is not a language feature in Rust. Instead, the gtk4 crate has Label impl IsA<Widget> (here) which allows you to call Cast::upcast like this:
Label::new(Some(label.property_value("name").get().unwrap())).upcast()
You might need to add
use gtk4::glib::object::Cast;
to the top of your file.
Instead you can also make your method return impl IsA<Widget> instead.
Related
I'm trying to make a tic tac toe game with rust and gtk, and in order to swap turns I'm toggling bool when a game button is clicked. Because of the rust/gtk integration, the onclick events run within an Fn closure, so I can't directly edit a mutable reference to the bool.
Based on this post, I moved from a basic bool to a bool within a cell as that has interior mutability, allowing it to be varied from within the closure.
However, the variable gets moved into the closure, and in the next iteration of the loop/usage of xturn I get
--> src/main.rs:45:44
|
39 | let xturn = Cell::new(true);
| ----- move occurs because `xturn` has type `std::cell::Cell<bool>`, which does not implement the `Copy` trait
...
45 | current_button.connect_clicked(move |current_button|{
| ^^^^^^^^^^^^^^^^^^^^^ value moved into closure here, in previous iteration of loop
46 | if current_button.label().unwrap() == ""{
47 | if xturn.get() {
| ----- use occurs due to use in closure
Code below
let game_grid: [[Button; 3];3] = [[new_game_button(), new_game_button(), new_game_button()],[new_game_button(), new_game_button(), new_game_button()],[new_game_button(), new_game_button(), new_game_button()]];
let xturn = Cell::new(true);
for i in 0..3{
for o in 0..3{
let current_button = &game_grid[i][o];
grid.attach(current_button, i.try_into().unwrap(), o.try_into().unwrap(), 1, 1);
current_button.connect_clicked(move |current_button|{
if current_button.label().unwrap() == ""{
if xturn.get() {
current_button.set_label("X");
} else{
current_button.set_label("O");
};
xturn.set(!xturn.get());
} else{
println!("This spot is taken! Go Again.");
}
});
}
}
Creating a reference to each use of xturn by adding & results in the same initial error message
Getting rid of the move at the start of the closure results in this error
error[E0373]: closure may outlive the current function, but it borrows `xturn`, which is owned by the current function
Complete code
use std::cell::Cell;
use gtk4 as gtk;
use gtk::{prelude::*, Label};
use gtk::{Align, Application, ApplicationWindow, Button, Grid};
fn main() {
let app = Application::builder()
.application_id("org.beribus.tictactoe")
.build();
app.connect_activate(build_ui);
println!("Hello, world!");
app.run();
}
fn build_ui(app: >k::Application){
let window = ApplicationWindow::builder()
.application(app)
.default_width(360)
.default_height(360)
.title("GTK Tac Toe")
.build();
let grid = Grid::builder()
.margin_top(10)
.margin_bottom(10)
.margin_start(10)
.margin_end(10)
.halign(gtk::Align::Center)
.valign(gtk::Align::Center)
.row_spacing(6)
.column_spacing(6)
.build();
window.set_child(Some(&grid));
let game_grid: [[Button; 3];3] = [[new_game_button(), new_game_button(), new_game_button()],[new_game_button(), new_game_button(), new_game_button()],[new_game_button(), new_game_button(), new_game_button()]];
let xturn = Cell::new(true);
for i in 0..3{
for o in 0..3{
let current_button = &game_grid[i][o];
grid.attach(current_button, i.try_into().unwrap(), o.try_into().unwrap(), 1, 1);
current_button.connect_clicked(move |current_button|{
if current_button.label().unwrap() == ""{
if xturn.get() {
current_button.set_label("X");
} else{
current_button.set_label("O");
};
&xturn.set(!&xturn.get());
} else{
println!("This spot is taken! Go Again.");
}
});
}
}
let text = Label::builder()
.label("woro")
.build();
if xturn.get(){
text.set_label("yoyooyo it's X's turn");
} else{
text.set_label("yoyooyo it's O's turn");
}
grid.attach(&text, 0,4,3,1);
let reset_button = Button::builder()
.halign(Align::Center)
.valign(Align::Center)
.label("Reset Game")
.build();
reset_button.connect_clicked(move |_|{
for i in 0..3{
for o in 0..3{
let current_button = &game_grid[i][o];
current_button.set_label("");
}
}
});
grid.attach(&reset_button, 0,3,3,1);
window.show();
}
fn new_game_button() -> Button{
let button = Button::builder()
.halign(Align::Center)
.valign(Align::Center)
.label("")
.width_request(90)
.height_request(90)
.build();
button
}
Both approaches are flawed, as the compiler indicated.
When you use the move approach, you are telling the closure that it can have xturn, but since this is in a loop, it can execute multiple times. You can't move the same value twice (since it is gone after the first time) and so the second move is disallowed.
When you try to capture by reference, the compiler is telling you that xturn belongs to the function in which it is declared, but the closures you are creating in the loop will outlive the function, and therefore the references will become invalid.
It looks like you want shared ownership of a single bool, which is implemented in Rust by the Rc struct, which performs reference-counting to determine when the shared value can be destroyed.
However, Rc does not permit mutable borrows while more than one Rc exists for the same shared value, so you still need the interior mutability of Cell. The final type that you want to use is Rc<Cell<bool>>. (RefCell isn't required here since you only need to be able to get and set the bool value; you don't ever need a reference to it.)
You need each iteration of the loop to move a copy of the Rc. Each separate Rc value will refer to the same Cell. For example, this function returns 9 closures that all do the same thing: they toggle the shared boolean value and return it in a tuple along with their own index:
fn create_closures() -> Vec<Box<dyn Fn() -> (i32, bool)>> {
let xturn = Rc::new(Cell::new(false));
(0..9).map(|i| -> Box<dyn Fn() -> (i32, bool)> {
// Make a clone of the Rc that this closure can steal.
let xturn = xturn.clone();
Box::new(move || {
let v = !xturn.get();
xturn.set(v);
(i, v)
})
}).collect()
}
(Playground)
I am trying to implement a simple web app using a web assembly created with wasm-bindgen. I am entirely new to Rust.
The web page would have a file input element to select a CSV file. The web-assembly code would load this CSV and use polars to group, sort, filter, etc. the data and then render the result back to the client(directly on the web page or by sharing data with the JS code)
Here is the code of the web assembly:
mod utils;
use wasm_bindgen::JsCast;
use wasm_bindgen::prelude::*;
use polars::prelude::*;
use std::io::Cursor;
use js_sys::JsString;
use polars_lazy::prelude::col;
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
#[wasm_bindgen]
pub fn load_file(file_input : web_sys::HtmlInputElement) {
let filelist = file_input.files().expect("Failed to get filelist from File Input!");
if filelist.length() < 1 {
return;
}
if filelist.get(0) == None {
return;
}
let file = filelist.get(0).expect("Failed to get File from filelist!");
let file_reader : web_sys::FileReader = match web_sys::FileReader::new() {
Ok(f) => f,
Err(e) => {
web_sys::FileReader::new().expect("")
}
};
let fr_c = file_reader.clone();
let onloadend_cb = Closure::wrap(Box::new(move |_e: web_sys::ProgressEvent| {
let data = fr_c.result().unwrap();
let file_string: JsString = data.dyn_into::<JsString>().unwrap();
let file_vec: Vec<u8> = file_string.iter().map(|x| x as u8).collect();
let cursor = Cursor::new(file_vec);
let df = CsvReader::new(cursor)
.has_header(true)
.finish().unwrap()
.groupby(col("name"));
}) as Box<dyn Fn(web_sys::ProgressEvent)>);
file_reader.set_onloadend(Some(onloadend_cb.as_ref().unchecked_ref()));
file_reader.read_as_array_buffer(&file).expect("blob not readable");
onloadend_cb.forget();
}
The compiler shows the error on line .groupby(col("name")):
error[E0277]: `Expr` is not an iterator
--> src\lib.rs:41:18
|
41 | .groupby(col("name"));
| ------- ^^^^^^^^^^^ expected an implementor of trait `IntoIterator`
| |
| required by a bound introduced by this call
|
I can't understand what's the reason for the error. It suggests adding "&" before the "col", but it does not help.
Does anyone know how to fix the error?
Looking at the docs for the DataFrame::groupby() method and the provided example, we can see that the column can be specified through an array of strings:
.groupby(["name"])
I'd like to use a SimpleActionGroup registered to a Widget that handles SimpleActions. But anyhow every Button that targets to Actions with my custom prefix are grayed out and can't be clicked. However Buttons that are targeting the action prefixes "app" and "win" are working as expected.
Application with two Buttons
Here is the part in my code where my custom prefix is defined.
// Create action group for view
let view_action_group = gio::SimpleActionGroup::new();
view.insert_action_group("view", &view_action_group);
// SimpleAction for view
let view_action = gio::SimpleAction::new("test", None);
view_action.connect_activate(|_, _| println!("view.test"));
view_action_group.add_action(&view_action);
Here I respond to SimpleActions with prefix "app"
// SimpleAction for app
let app_action = gio::SimpleAction::new("test", None);
app_action.connect_activate(|_, _| println!("app.test"));
app.add_action(&app_action);
The SimpleAction itself should be ok, because if I start the application with GTK Inspector I can activate it.
GTK Inspector invoking actions
So what am I missing that the secound Button is not greyed out and responds to my SimpleAction?
Here is the full code:
extern crate gtk;
extern crate gio;
use std::env::args;
use gtk::{GtkApplicationExt, GtkWindowExt, ContainerExt, WidgetExt, HeaderBarExt, ActionableExt};
use gio::{SimpleActionExt, ActionMapExt, ApplicationExt, ApplicationExtManual};
struct Window {
pub window: gtk::Window,
}
impl Window {
pub fn new() -> Self {
// Create Widgets and window
let header_bar = gtk::HeaderBar::new();
let window = gtk::Window::new(gtk::WindowType::Toplevel);
let view = gtk::Box::new(gtk::Orientation::Horizontal, 5);
let app_button = gtk::Button::new_with_label("App-Action-Button");
let view_button = gtk::Button::new_with_label("View-Action-Button");
// Create Action Group for view
let view_action_group = gio::SimpleActionGroup::new();
view.insert_action_group("view", &view_action_group);
// SimpleAction for view
let view_action = gio::SimpleAction::new("test", None);
view_action.connect_activate(|_, _| println!("view.test"));
view_action_group.add_action(&view_action);
// Set Action for Buttons
app_button.set_action_name("app.test");
view_button.set_action_name("view.test");
// Configure HeaderBar and add Buttons to it
header_bar.set_show_close_button(true);
header_bar.pack_start(&app_button);
header_bar.pack_start(&view_button);
// Window Stuff
window.set_titlebar(&header_bar);
window.add(&view);
window.show_all();
Self { window }
}
}
struct App {
app: gtk::Application
}
impl App {
pub fn new () -> Self {
// Create app and window
let app = gtk::Application::new(Some("com.example.test"), gio::ApplicationFlags::FLAGS_NONE).unwrap();
let window = Window::new();
// SimpleAction for app
let app_action = gio::SimpleAction::new("test", None);
app_action.connect_activate(|_, _| println!("app.test"));
app.add_action(&app_action);
// add Signal
let cloned_window = window.window.clone();
app.connect_activate(move |app| app.add_window(&cloned_window));
Self { app }
}
pub fn run(&self, args: &[String]) {
self.app.run(args);
}
}
fn main() {
let app = App::new();
app.run(&args().collect::<Vec<_>>());
}
I am just starting to learn Rust. For this purpose I am rewriting my C++ project in Rust, but the biggest problems are lifetimes of closures and such.
I created a absolute minimal scenario of my problem seen here and below:
use std::sync::Arc;
use std::cell::{RefCell, Cell};
struct Context {
handler: RefCell<Option<Arc<Handler>>>,
}
impl Context {
pub fn new() -> Arc<Context> {
let context = Arc::new(Context{
handler: RefCell::new(None),
});
let handler = Handler::new(context.clone());
(*context.handler.borrow_mut()) = Some(handler);
context
}
pub fn get_handler(&self) -> Arc<Handler> {
self.handler.borrow().as_ref().unwrap().clone()
}
}
struct Handler {
context: Arc<Context>,
clickables: RefCell<Vec<Arc<Clickable>>>,
}
impl Handler {
pub fn new(context: Arc<Context>) -> Arc<Handler> {
Arc::new(Handler{
context: context,
clickables: RefCell::new(Vec::new()),
})
}
pub fn add_clickable(&self, clickable: Arc<Clickable>) {
self.clickables.borrow_mut().push(clickable);
}
pub fn remove_clickable(&self, clickable: Arc<Clickable>) {
// remove stuff ...
}
}
struct Clickable {
context: Arc<Context>,
callback: RefCell<Option<Box<Fn()>>>,
}
impl Clickable {
pub fn new(context: Arc<Context>) -> Arc<Clickable> {
let clickable = Arc::new(Clickable{
context: context.clone(),
callback: RefCell::new(None),
});
context.get_handler().add_clickable(clickable.clone());
clickable
}
pub fn remove(clickable: Arc<Clickable>) {
clickable.context.get_handler().remove_clickable(clickable);
}
pub fn set_callback(&self, callback: Option<Box<Fn()>>) {
(*self.callback.borrow_mut()) = callback;
}
pub fn click(&self) {
match *self.callback.borrow() {
Some(ref callback) => (callback)(),
None => (),
}
}
}
struct Button {
context: Arc<Context>,
clickable: Arc<Clickable>,
}
impl Button {
pub fn new(context: Arc<Context>) -> Arc<Button> {
let clickable = Clickable::new(context.clone());
let button = Arc::new(Button{
context: context,
clickable: clickable.clone(),
});
let tmp_callback = Box::new(|| {
button.do_stuff();
});
clickable.set_callback(Some(tmp_callback));
button
}
pub fn do_stuff(&self) {
// doing crazy stuff
let mut i = 0;
for j in 0..100 {
i = j*i;
}
}
pub fn click(&self) {
self.clickable.click();
}
}
impl Drop for Button {
fn drop(&mut self) {
Clickable::remove(self.clickable.clone());
}
}
fn main() {
let context = Context::new();
let button = Button::new(context.clone());
button.click();
}
I just don't know how to pass references in closures.
Another ugly thing is that my Handler and my Context need each other. Is there a nicer way to to create this dependency?
Going off your initial code
pub fn new(context: Arc<Context>) -> Arc<Button> {
let clickable = Clickable::new(context.clone());
let button = Arc::new(Button{
context: context,
clickable: clickable.clone(),
});
let tmp_callback = Box::new(|| {
button.do_stuff();
});
clickable.set_callback(Some(tmp_callback));
button
}
First off, let's note the error you're getting
error[E0373]: closure may outlive the current function, but it borrows `button`, which is owned by the current function
--> src/main.rs:101:37
|
101 | let tmp_callback = Box::new(|| {
| ^^ may outlive borrowed value `button`
102 | button.do_stuff();
| ------ `button` is borrowed here
|
help: to force the closure to take ownership of `button` (and any other referenced variables), use the `move` keyword, as shown:
| let tmp_callback = Box::new(move || {
Noting the help block at the bottom, you need to use a move closure, because when the new function ends, the button variable on the stack will go out of scope. The only way to avoid that is to move ownership of it to the callback itself. Thus you'd change
let tmp_callback = Box::new(|| {
to
let tmp_callback = Box::new(move || {
Now, you'd get a second error:
error[E0382]: use of moved value: `button`
--> src/main.rs:107:9
|
102 | let tmp_callback = Box::new(move || {
| ------- value moved (into closure) here
...
107 | button
| ^^^^^^ value used here after move
|
= note: move occurs because `button` has type `std::sync::Arc<Button>`, which does not implement the `Copy` trait
And the error here may be a little clearer. You're trying to move ownership of the button value into the callback closure, but you also use it inside the body of the new function when you return it, and you can't have two different things trying to own the value.
The solution to that is hopefully what you'd guess. You have to make a copy that you can take ownership of. You'll want to then change
let tmp_callback = Box::new(move || {
button.do_stuff();
to
let button_clone = button.clone();
let tmp_callback = Box::new(move || {
button_clone.do_stuff();
Now you've created a new Button object, and returned an Arc for the object itself, while also giving ownership of a second Arc to the callback itself.
Update
Given your comment, there is indeed an issue here of cyclic dependencies, since your Clickable object holds ownership of a reference to Button, while Button holds ownership of a reference to Clickable. The easiest way to fix this here would be to update that code a third time, from
let button_clone = button.clone();
let tmp_callback = Box::new(move || {
button_clone.do_stuff();
to
let button_weak = Arc::downgrade(&button);
let tmp_callback = Box::new(move || {
if let Some(button) = button_weak.upgrade() {
button.do_stuff();
}
});
so the Clickable will only hold a weak reference to the Button, and if the Button is no longer referenced, the callback will be a no-op.
You'd also probably want to consider making clickables a list of Weak references instead of strong references, so you can remove items from it when the item they reference is removed.
I'm trying to use the hyper library to make some requests. The Headers::get() method returns Option<&H>, where H is a tuple struct with one field. I can use if let Some() to destructure the Option. But how do we destructure the &H? Sure I could always access the field with .0, but I'm curious if Rust has a syntax to do this.
struct s(String);
fn f(input: &s) -> &s {
input
}
fn main() {
let my_struct1 = s("a".to_owned());
let s(foo) = my_struct1;
let my_struct2 = s("b".to_owned());
let &s(bar) = f(&my_struct2); // this does not work
let baz = &my_struct2.0; // this works
}
When you try to compile this, the Rust compiler will tell you how to fix the error with a nice message:
error[E0507]: cannot move out of borrowed content
--> <anon>:11:9
|
11 | let &s(bar) = f(&my_struct2); // this does not work
| ^^^---^
| | |
| | hint: to prevent move, use `ref bar` or `ref mut bar`
| cannot move out of borrowed content
This is needed to tell the compiler that you only want a reference to the field in the struct; the default matching will perform a move and the original struct value will no longer be valid.
Let's fix the example:
struct s(String);
fn f(input: &s) -> &s {
input
}
fn main() {
let my_struct1 = s("a".to_owned());
let s(foo) = my_struct1;
let my_struct2 = s("b".to_owned());
let &s(ref bar) = f(&my_struct2);
}
Another way is to dereference first and drop the &. I think this is preferred in Rust:
struct s(String);
fn f(input: &s) -> &s {
input
}
fn main() {
let my_struct1 = s("a".to_owned());
let s(foo) = my_struct1;
let my_struct2 = s("b".to_owned());
let s(ref bar) = *f(&my_struct2);
}