New to Rust, really feel like I'm missing something here.
In the below code, there are two nested closures which are important to the question: app.connect_activate and draw_area.connect_draw. I am trying to use a channel to send values into the connect_draw closure, so that what is drawn will reflect the state of a simulation that changes over time (currently represented by random colors).
What I've found is very weird to me and I do not understand at all:
if I create the channel within connect_activate, I can call rx.recv within connect_draw (this is the first example below)
if I create the channel outside of connect_activate, I can call rx.recv within connect_activate (example omitted for brevity)
if I create the channel outside of connect_activate, I cannot call rx.recv within connect_draw; it fails with cannot move out of rx, a captured variable in an Fn closure (this is the second example below)
In other words: I can move the created channel across either closure boundary, but not both. It can move from the scope of main into the scope of connect_activate, or from the scope of connect_activate into the scope of connect_draw, but it cannot do both in sequence. Why would this be the case? If I can move it into connect_activate, shouldn't it then be just as "owned" as if it were created there? Both cases do seem to be truly "moved", insofar as they fail if I omit the "move" keyword. What's going on here?
Bonus question: There's a hideous placeholder where I'm re-drawing every time I get any event, because I still don't know how to run events off a timer. Halp?
Example 1: (WORKS)
extern crate cairo;
extern crate rand;
extern crate gtk;
extern crate gdk;
extern crate glib;
use std::{thread, time};
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, DrawingArea};
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
fn main(){
let app = Application::builder()
.application_id("org.example.HelloWorld")
.build();
app.connect_activate(move |app| {
let (tx, rx ) : (Sender<f64>, Receiver<f64>)= mpsc::channel();
let draw_area = DrawingArea::new();
let _id = draw_area.connect_draw(move |_unused, f| {
let red = rx.recv().unwrap();
let green = rx.recv().unwrap();
let blue = rx.recv().unwrap();
f.set_source_rgb(red,green, blue);
f.paint().expect("Painting failed");
Inhibit(false)
});
let win = ApplicationWindow::builder()
.application(app)
.default_width(320)
.default_height(200)
.title("Hello, World!")
.build();
win.add(&draw_area);
win.show_all();
win.connect_event(|w, _g|{ //Placeholder until I learn to make queue_draw fire on a timer
w.queue_draw();
Inhibit(false)
});
thread::spawn(move || {
loop {
thread::sleep(time::Duration::from_millis(100));
tx.send(rand::random::<f64>()).unwrap();
}
});
});
app.run();
}
Example 2 (DOES NOT WORK)
extern crate cairo;
extern crate rand;
extern crate gtk;
extern crate gdk;
extern crate glib;
use std::{thread, time};
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, DrawingArea};
use std::sync::mpsc;
use std::sync::mpsc::{Receiver, Sender};
fn main(){
let app = Application::builder()
.application_id("org.example.HelloWorld")
.build();
let (tx, rx ) : (Sender<f64>, Receiver<f64>)= mpsc::channel();
app.connect_activate(move |app| {
let draw_area = DrawingArea::new();
let _id = draw_area.connect_draw(move |_unused, f| {
let red = rx.recv().unwrap();
let green = rx.recv().unwrap();
let blue = rx.recv().unwrap();
f.set_source_rgb(red,green, blue);
f.paint().expect("Painting failed");
Inhibit(false)
});
let win = ApplicationWindow::builder()
.application(app)
.default_width(320)
.default_height(200)
.title("Hello, World!")
.build();
win.add(&draw_area);
win.show_all();
win.connect_event(|w, _g|{ //Placeholder until I learn to make queue_draw fire on a timer
w.queue_draw();
Inhibit(false)
});
});
thread::spawn(move || {
loop {
thread::sleep(time::Duration::from_millis(100));
tx.send(rand::random::<f64>()).unwrap();
}
});
app.run();
}
A gtk application's connect_activate() method takes in a Fn parameter, meaning it can be called multiple times. You cannot move a captured variable out of a Fn closure since the variable will have been moved next time you tried to call it. The first example works because it only moves a local variable that would be created each time.
Related
A couple months ago, I started writing an X11 window manager in C with Xlib and I'm now trying to rewrite it in Rust. (after all, rust is the future, right?)
This is the code from main.rs that segfaults:
// event loop
loop {
let mut event : *mut XEvent = 0 as *mut XEvent;
unsafe {
XNextEvent(DISPLAY, event);
/*match (*event).type_ {
CreateNotify => {
println!("sdfg");
}
_ => {
println!("sdfgsdfg");
}
}*/
}
println!("dsfgsdfg");
}
Using GDB, I was able to narrow it down to XNextEvent inside the unsafe block. I'm not really sure where to go from there, since it's calling a function /use/lib/libX11.so.6 and I can't list the function to see where it quit.
Before you ask about the unsafe block, I can't put Xlib functions outside of unsafe blocks because they are unsafe functions and the compiler will complain.
The project uses the x11 crate on crates.io.
You are passing a pointer to NULL instead of a pointer to a local variable to be filled by XNextEvent, just as if your C code did:
XEvent *event = NULL;
XNextEvent(DISPLAY, event); //crash!
In C, you are probably doing:
XEvent event;
XNextEvent(DISPLAY, &event);
But unfortunately you cannot do that directly in Rust because you cannot declare an uninitialized variable:
let mut event: XEvent;
XNextEvent(DISPLAY, &mut event); //error: event is not initialized
If XEvent implemented Default you could just default initialize it, but I think it does not.
For these cases you can use MaybeUninit:
let event = unsafe {
let mut event = MaybeUninit::uninit();
XNextEvent(DISPLAY, event.as_mut_ptr());
event.assume_init()
};
I'm making a mini web browser with gtk. This code works fine, but I want to avoid all the cloning that is using.
I can't just use the web_view object multiple times because of ownership errors on the closures.
extern crate gio;
extern crate gtk;
extern crate webkit2gtk;
use gio::prelude::*;
use gtk::prelude::*;
use gtk::{Application, ApplicationWindow, Button, Entry};
use webkit2gtk::{WebView, WebViewExt};
fn main() {
let application =
Application::new(Some("com.github.gtk-rs.examples.basic"), Default::default())
.expect("failed to initialize GTK application");
application.connect_activate(|app| {
let window = ApplicationWindow::new(app);
window.set_title("Web Browser");
window.set_default_size(1024, 768);
let button_back = Button::new_with_label("Back");
let button_next = Button::new_with_label("Next");
let button_reload = Button::new_with_label("Reload");
let url_entry = Entry::new();
url_entry.set_text("https://www.google.com");
let button_go = Button::new_with_label("Go");
let web_view = WebView::new();
web_view.load_uri("https://www.google.com");
let clone1: WebView = web_view.clone();
let clone2: WebView = web_view.clone();
let clone3: WebView = web_view.clone();
let clone4: WebView = web_view.clone();
button_back.connect_clicked(move |_| {
clone1.go_back();
});
button_next.connect_clicked(move |_| {
clone2.go_forward();
});
button_reload.connect_clicked(move |_| {
clone3.reload();
});
button_go.connect_clicked(move |_| {
clone4.load_uri(&url_entry.get_buffer().get_text());
});
window.show_all();
});
application.run(&[]);
}
These are my dependency versions:
[dependencies]
gtk = "0.8.1"
gio = "0.8.1"
webkit2gtk = "0.9.2"
The signature for the method connect_clicked of the trait gtk::ButtonExt is
fn connect_clicked<F: Fn(&Self) + 'static>(&self, f: F) -> SignalHandlerId
No, there isn't.
'static on the callback definition forbids it from referencing any temporary variables, and forces it to move and own any values it uses. That's deliberate, because the callback will outlive the function that creates it.
gtk's WebView behaves like Rc, so the clone is very cheap, and doesn't make a copy of anything. It only performs bookkeeping to help the callbacks agree which one of them will be the last to be destroyed, and therefore the one to free the underlying WebView data.
There's a clone! macro in glib that helps making these clones without having to name extra variables by hand.
I am trying to get a value from a thread, in this case a HashMap. I reduced the code to the following (I originally tried to share a HashMap containig a Vec):
use std::thread;
use std::sync::mpsc;
use std::sync::Mutex;
use std::sync::Arc;
use std::collections::HashMap;
fn main() {
let(tx, rx) = mpsc::channel();
let n_handle= thread::spawn( || {
tx.send(worker());
});
print!("{:?}", rx.recv().unwrap().into_inner().unwrap());
}
fn worker() -> Arc<Mutex<HashMap<String, i32>>>{
let result: HashMap<String, i32> = HashMap::new();
// some computation
Arc::from(Mutex::from(result))
}
Still Rust says that:
std::sync::mpsc::Sender<std::sync::Arc<std::sync::Mutex<std::collections::HashMap<std::string::String, i32>>>> cannot be shared between threads safely
I read some confusing stuff about putting everything into Arc<Mutex<..>> which I also tried with the value:
let result: HashMap<String, Arc<Mutex<i32>>> = HashMap::new();
Can anyone point me to a document that explains the usage of the mpsc::channel with values such as HashMaps? I understand why it is not working, as the trait Sync is not implemented for the HashMap, which is required to share the stuff. Still I have no idea how to get it to work.
You can pass the values between threads with using mpsc channel.
Until you tag your thread::spawn with the move keyword like following:
thread::spawn(move || {});
Since you did not tag it with move keyword then it is not moving the outer variables into the thread scope but only sharing their references. Thus you need to implement Sync trait that every outer variable you use.
mpsc::Sender does not implement Sync that is why you get the error cannot be shared between threads.
The solution for your case would be ideal to move the sender to inside of the thread scope with move like following:
use std::collections::HashMap;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let _ = tx.send(worker());
});
let arc = rx.recv().unwrap();
let hashmap_guard = arc.lock().unwrap();
print!(
"HashMap that retrieved from thread : {:?}",
hashmap_guard.get("Hello").unwrap()
);
}
fn worker() -> Arc<Mutex<HashMap<String, i32>>> {
let mut result: HashMap<String, i32> = HashMap::new();
result.insert("Hello".to_string(), 2);
// some computation
Arc::new(Mutex::new(result))
}
Playground
For further info: I'd recommend reading The Rust Programming Language, specifically the chapter on concurrency. In it, you are introduced to Arc: especially if you want to share your data in between threads.
Here is an example program:
extern crate futures;
extern crate tokio_core;
use futures::{Async, Future, Stream};
use tokio_core::reactor::Core;
use tokio_core::net::TcpListener;
fn main() {
let mut core = Core::new().unwrap();
futures::sync::oneshot::spawn(
TcpListener::bind(&"127.0.0.1:5000".parse().unwrap(), &core.handle())
.unwrap()
.incoming()
.for_each(|_| {
println!("connection received");
Ok(())
}),
&core,
);
let ft = futures::future::poll_fn::<(), (), _>(|| {
std::thread::sleep_ms(50);
Ok(Async::NotReady)
});
core.run(ft);
}
As you can see, I call oneshot::spawn and then immediately drop its return value, which should theoretically cancel the future contained inside. However, when I run this program and then make a connection to 127.0.0.1:5000, it still prints "connection received." Why does it do this? I expected it to not print anything and drop the TcpListener, unbinding from the port.
This is a (now fixed) bug in the futures crate; version 0.1.18 should include the fix.
It used inverted values for keep_running: bool in SpawnHandle/Executor.
Here's a small example:
extern crate hlua;
use hlua::*;
use std::rc::Rc;
use std::cell::{Cell, RefCell};
fn main() {
let lua = Rc::new(RefCell::new(Lua::new()));
lua.borrow_mut().set("x", 42);
{
let lua2 = lua.clone();
lua.borrow_mut().set("foo", function0(move ||{
lua2.borrow_mut().set("x", 1); // XXX thread 'main' panicked at 'already borrowed: BorrowMutError'
}));
}
let _ = lua.borrow_mut().execute::<()>("foo()");
println!("{}", lua.borrow_mut().get::<i32, _>("x").unwrap());
}
This code panics at runtime at the line marked XXX. I get that lua is borrowed when I call foo(), but I don't see other way to do it. How can I use lua from inside an exported function?