I'm building an ncurses interface for a little thing I'm working on. For the input, I want to read it non blocking. I figured I could wrap the WINDOW in an Arc Mutex but this doesn't seem to work, as it still complains about send. Is this because the implementation of ncurses is unsafe? How can I solve this? Ideally, I'd have this work with a callback instead of the tx, so I can cut the dependency from the view up the stack, but I couldn't get that closure to Send either.
I'm using this library: https://github.com/jeaye/ncurses-rs
Simplified code:
pub struct View {
max_x: i32,
max_y: i32,
messages_window: WINDOW,
input_window: Arc<Mutex<WINDOW>>
}
pub fn init(&mut self, input_tx: mpsc::Sender<DispatchMessage>) {
let input_window = self.input_window.clone();
thread::spawn(move || {
loop {
let input_window = input_window.lock().unwrap();
draw_prompt(input_window);
let input = prompt_input(input_window);
input_tx.send(input_to_message(input)).unwrap();
}
});
}
fn prompt_input(input: WINDOW) -> String {
let mut string = String::new();
wgetstr(input, &mut string);
string
}
fn draw_prompt(input: WINDOW) {
wclear(input);
let top = 0 as chtype;
let bottom = ' ' as chtype;
let empty = ' ' as chtype;
wborder(input, empty,empty,top,bottom,empty,empty,empty,empty);
mvwprintw(input, 1, 1, ">> ");
wrefresh(input);
}
And the errors I get:
src/view.rs:40:33: 40:45 error: mismatched types:
expected `*mut ncurses::ll::WINDOW_impl`,
found `std::sync::mutex::MutexGuard<'_, *mut ncurses::ll::WINDOW_impl>`
(expected *-ptr,
found struct `std::sync::mutex::MutexGuard`) [E0308]
src/view.rs:40 draw_prompt(input_window);
^~~~~~~~~~~~
note: in expansion of closure expansion
src/view.rs:37:27: 44:14 note: expansion site
src/view.rs:40:33: 40:45 help: run `rustc --explain E0308` to see a detailed explanation
src/view.rs:41:46: 41:58 error: mismatched types:
expected `*mut ncurses::ll::WINDOW_impl`,
found `std::sync::mutex::MutexGuard<'_, *mut ncurses::ll::WINDOW_impl>`
(expected *-ptr,
found struct `std::sync::mutex::MutexGuard`) [E0308]
src/view.rs:41 let input = prompt_input(input_window);
^~~~~~~~~~~~
note: in expansion of closure expansion
src/view.rs:37:27: 44:14 note: expansion site
src/view.rs:41:46: 41:58 help: run `rustc --explain E0308` to see a detailed explanation
src/view.rs:37:13: 37:26 error: the trait `core::marker::Send` is not implemented for the type `*mut ncurses::ll::WINDOW_impl` [E0277]
src/view.rs:37 thread::spawn(move || {
^~~~~~~~~~~~~
src/view.rs:37:13: 37:26 note: `*mut ncurses::ll::WINDOW_impl` cannot be sent between threads safely
src/view.rs:37 thread::spawn(move || {
^~~~~~~~~~~~~
error: aborting due to 3 previous errors
Manually dereferencing the window removes the type errors, but I figured I'd leave it in as it might be an indication of what's wrong.
Arc<T>
implementsSend
whereT
implements bothSend
andSync
.Mutex<T>
implementsSend
andSync
whereT
implementsSend
. SoArc<Mutex<T>>
only implementsSend
ifT
implementsSend
. Remember thatSend
means “Types able to be transferred across thread boundaries.”Arc<Mutex<T>>
allows access to its contents from multiple threads, purely taking care of ownership and mutability issues, so if the underlying type cannot be transferred across thread boundaries it won’t help. You may well need to do all your ncurses operations from one thread.Raw pointers explicitly do not implement
Send
because there can be no guarantees about it. You can construct types on top of it which explicitly implementSend
, thus providing a guarantee that the contained raw pointer is in fact safe for transferring across thread boundaries.