I'm trying to implement the game "Tower of Hanoi" using concurrency in Rust. Honestly, I tried to understand the whole lifetime thing in Rust in the last time, but I'm not done yet. That's why I get some weird lifetime errors which I don't understand. First, here is the important piece of code
fn move_plate<'a>(stack_a: &'a mut Vec<i32>, stack_b: &'a mut Vec<i32>,
stack_c: &'a mut Vec<i32>, moves: &'a mut Vec<(i32, i32)>)
{
let mut moves1: Vec<(i32, i32)> = Vec::new();
let guard1 = Thread::scoped(
move || { move_plate(stack_a, stack_c, stack_b, (1, 3, 2), &mut moves1);
});
guard1.join().ok();
}
And here is the error
error: cannot infer an appropriate lifetime due to conflicting requirements
let guard1 = Thread::scoped(move || {
move_plate(height - 1, stack_a, stack_c, stack_b, (1, 3, 2), threads, depth + 1, &mut moves1);
});
note: first, the lifetime cannot outlive the expression at 93:25...
let guard1 = Thread::scoped(move || {
note: ...so that the declared lifetime parameter bounds are satisfied
let guard1 = Thread::scoped(move || {
note: but, the lifetime must be valid for the expression at 93:45...
let guard1 = Thread::scoped(move || {
move_plate(height - 1, stack_a, stack_c, stack_b, (1, 3, 2), threads, depth + 1, &mut moves1);
});
note: ...so type `closure[]` of expression is valid during the expression
let guard1 = Thread::scoped(move || {
move_plate(height - 1, stack_a, stack_c, stack_b, (1, 3, 2), threads, depth + 1, &mut moves1);
});
error: declared lifetime bound not satisfied
let guard1 = Thread::scoped(move || {
I understand that I have to avoid the thread to outlive the function, because otherwise the reference to moves would be gone. But since I join the thread, that should be all right, shouldn't it? What am I missing at that point?
Would be really nice if somebody could help me, I'm just getting used to that cool (but complicated) kind of stuff
This is a known limitation of the Rust type system. Currently Rust allows to send data between threads only if this data satisfies Send bound, and Send implies 'static - that is, the only references which can be sent across thread boundaries are 'static ones.
There is an RFC which partially lifts this limitation, allowing non-'static references to be sent across tasks. I thought that it already has been accepted, but it is not (which is strange). The API that supports such thing has already been created (and this is likely why you are confused), but the language is yet to be adjusted.
Related
I've been writing an app in Rust that uses some OpenGL, and I've observed a trend in how OpenGl is accessed/managed in rust code. Frequently it seems that managing or creating an OpenGl context requires unsafe.
Why do these examples require unsafe code? I haven't been running into any problems because of this unsafe designator, but I'm just curious as to why its there. What kind of problems or constraints do these unsafe requirements impose on developers?
from glutins Multi-Window example
//...
let mut windows = std::collections::HashMap::new();
for index in 0..3 {
let title = format!("Charming Window #{}", index + 1);
let wb = WindowBuilder::new().with_title(title);
let windowed_context = ContextBuilder::new().build_windowed(wb, &el).unwrap();
//uses unsafe code
let windowed_context = unsafe { windowed_context.make_current().unwrap() };
let gl = support::load(&windowed_context.context());
let window_id = windowed_context.window().id();
let context_id = ct.insert(ContextCurrentWrapper::PossiblyCurrent(
ContextWrapper::Windowed(windowed_context),
));
windows.insert(window_id, (context_id, gl, index));
}
//...
from fltk-rs glow demo
//...
unsafe {
let gl = glow::Context::from_loader_function(|s| {
win.get_proc_address(s) as *const _
});
let vertex_array = gl
.create_vertex_array()
.expect("Cannot create vertex array");
gl.bind_vertex_array(Some(vertex_array));
let program = gl.create_program().expect("Cannot create program");
//...
win.draw(move |w| {
gl.clear(glow::COLOR_BUFFER_BIT);
gl.draw_arrays(glow::TRIANGLES, 0, 3);
w.swap_buffers();
});
}
//...
OpenGL is implemented as a library with a C ABI. If you want to call a C function from rust, it always means you have to use unsafe because the C implementation knows nothing about the safety features of rust and naturally doesn't support them. Furthermore, OpenGL uses raw pointers in its API to pass data from or to the GL, which also requires unsafe code in rust.
Am reading through (the) C++ Core guidelines and encountered this rule: "Don’t declare a variable until you have a value to initialize it with" https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es22-dont-declare-a-variable-until-you-have-a-value-to-initialize-it-with
It describes the following code as bad:
SomeLargeType var;
if (cond) // some non-trivial condition
Set(&var);
else if (cond2 || !cond3) {
var = Set2(3.14);
}
else {
var = 0;
for (auto& e : something)
var += e;
}
Unfortunately this point fails to describe a way to how to solve this exact issue. Sometimes you just have to initialize a large object differently depending on a condition.
The only circumvent that comes to my mind is something like:
SomeLargeType * var;
if (cond) // some non-trivial condition
var = new SomeLargeType(123);
else if (cond2 || !cond3) {
var = new SomeLargeType(3.14);
}
However even if I use a smartpointer, this feels somehow unnecessary/unsafe and most of all, worse than the initial way.
What is the optimal solution?
You can use a function. Also, don't use bare pointers with ownership (I assume that there's a guideline for this too). Example:
std::unique_ptr<SomeLargeType>
make_something(bool cond, bool cond23)
{
if (cond)
return std::make_unique<SomeLargeType>(123);
else if (cond23)
return std::make_unique<SomeLargeType>(3.14);
else
return nullptr;
}
// usage
std::unique_ptr<SomeLargeType> var = make_something(cond, cond2 || !cond3);
If there's no way for this function to be reusable, then a lambda might be appropriate, as shown by Sopel
First, there is no undefined behaviour in the original sample code: all possible conditions are taken care of (by the final else block). However, there is potential undefined behaviour in the answers so far given, as they replace the 'good' code in the aforementioned else block with, effectively, no initialisation and the return of a nullptr, which may later be the subject of an attempted dereference.
Also, there is no real need here to complicate matters by replacing the instance variable with a pointer (and this also changes the nature/logic of the code).
Using a lambda (as suggested in the link provided by StaceyGirl) is certainly a good way to go (probably the best, but that may be subjective). However, to keep the logic the same as the original code, one can apply the lambda to the object, rather than to a pointer, as so:
SomeLargeType var = [&]() {
if (cond) { // some non-trivial condition
SomeLargeType v1;
Set(&v1);
return v1;
}
else if (cond2 || !cond3) {
SomeLargeType v2 = Set2(3.14);
return v2;
}
else {
SomeLargeType v3 = 0;
for (auto& e : something) var += e;
return v3;
}
}();
Here, unlike in the original code (where a default constructor is first called, then one of three others), a constructor for SomeLargeObject will only be called once¹ and there will be no undefined behaviour. It is this initial call to the (potentially very expensive) default constructor that, I presume, is the reason for this being cited as an example of "bad code."
¹ If there are any doubts about how often the constructors are called, I can provide a complete MCVE (with some minor modifications to avoid the undefined for (auto& e : something) line), if such is requested.
An immediately invoked lambda can be used as an alternative to a named function.
SomeLargeType* var = [&]() {
if (cond) // some non-trivial condition
return new SomeLargeType(123);
else if (cond2 || !cond3)
return new SomeLargeType(3.14);
else
return nullptr;
}();
Note that some types may not provide a default constructor, in which case it's impossible to have a lazy initialization without using a boxing type.
This can also improve performance when the type is not trivially constructible.
https://godbolt.org/z/_V8t2T
I'm working on a function that looks like this:
fn do_stuff(&mut self, a: MyStruct) -> Result<(), MyError> {
let x = try!(serde_json::to_vec(a));
let cache = Arc::clone(self.data); // Get shared reference
{
let cache = try!(cache.lock()); // Get lock
cache.push(x);
}
/* Do stuff with other resources */
Ok(())
}
Where the definition of MyError is:
#[derive(Debug)]
pub enum MyError {
Serialization(serde_json::Error),
Synch(PoisonError<MutexGuard<'_, Vec<u8>>>),
}
Before I even get to implementing From<std::sync::PoisonError> for MyError, the compiler already tells me the definition of the Synch variant of my enum is wrong:
error: underscore lifetimes are unstable (see issue #44524)
The declaration using underscore lifetimes actually came from an earlier hint from the compiler when I was trying to figure out the error I should convert from when the lock operation fails. I read the aforementioned issue and that doesn't help me.
What's the full type I should be converting from in order to catch the error from the Mutex::lock operation?
Like so:
#[derive(Debug)]
pub enum MyError<'a> {
Serialization(serde_json::Error),
Synch(PoisonError<MutexGuard<'a, Vec<u8>>>),
}
The closest explanation I can find in the book is the section on Lifetime Annotations in Struct Definitions (enums behave the same way).
The compiler suggesting unstable syntax as a solution is quite unfair.
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> implements Send where T implements both Send and Sync. Mutex<T> implements Send and Sync where T implements Send. So Arc<Mutex<T>> only implements Send if T implements Send. Remember that Send 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 implement Send, thus providing a guarantee that the contained raw pointer is in fact safe for transferring across thread boundaries.
I am working on code for a library that uses Eigen extensively, and frequently maps Eigen::Matrix objects with an NSObject subclass of my own design (vMAT_Array). Working with the library often requires marshaling matrices into vMAT_Array instances to they can be passed around, etc.
I have a vMAT_cast template function which handles this:
template <typename EigenObjectType>
vMAT_Array * vMAT_cast(EigenObjectType matrix)
{
return Map<EigenObjectType>(matrix).matA;
}
The problem with this function is that it does not interact correctly with Eigen's lazy evaluation semantics. Take for example the unit test code below:
vMAT_Array * matM = vMAT_cast(VectorXd::LinSpaced(40, 1.0, 40.0).eval());
[matM reshape:vMAT_MakeSize(5, 8)];
Mat<double> M = matM;
Array<bool, Dynamic, Dynamic> sel = M.unaryExpr([](double elt) { return (int)elt % 3 == 0; }).cast<bool>();
vMAT_Array * vecN = vMAT_pick(matM, vMAT_cast(sel));
NSLog(#"%#", vecN.dump);
vMAT_Array * vecNv = vMAT_cast(VectorXd::LinSpaced(13, 3.0, 39.0).eval());
STAssertEqualObjects(vecN, vecNv, #"Logical indexing broken");
Notice the explicit .eval() calls on most of the arguments to vMAT_cast. These are necessary because the template function tries (at compile time) to expand into code using one of Eigen's lazy expression templates, which generates lovely error messages like this one:
/Users/Shared/Source/vMAT/vMATTests/EigenTests.mm:25:35: note: in instantiation of member function 'Eigen::DenseBase<Eigen::Matrix<double, -1, 1, 0, -1, 1> >::LinSpaced' requested here
vMAT_Array * matM = vMAT_cast(VectorXd::LinSpaced(40, 1.0, 40.0));
^
In file included from /Users/Shared/Source/vMAT/vMATTests/EigenTests.mm:11:
In file included from /Users/Shared/Source/vMAT/vMAT/vMAT.h:51:
/Users/Shared/Source/vMAT/vMAT/vMAT_Array.h:122:82: error: no member named 'data' in 'Eigen::CwiseNullaryOp<Eigen::internal::linspaced_op<double, true>, Eigen::Matrix<double, -1, 1, 0, -1, 1> >'
data:[NSMutableData dataWithBytes:matrix.data()
~~~~~~ ^
I suspect there is template-fu that would "force" the MatrixBase::eval to happen, but I lack knowledge of it. Can anyone enlighten me?
You can find this kind of template kung-fu in eigen/unsupported/Eigen/OpenGLSupport module. Here you will find wrappers to OpenGL functions taking vectors and matrices through raw C pointers. The key is in this test where we check whether the expression type is OpenGL compatible:
bool IsGLCompatible = bool(XprType::Flags&LinearAccessBit)
&& bool(XprType::Flags&DirectAccessBit)
&& (XprType::IsVectorAtCompileTime || (XprType::Flags&RowMajorBit)==0)
LinearAccessBit means there is no "stride", DirectAccessBit means there is a .data() available. There other two are obvious and maybe not relevant in your case.
Another option for you, probably much simpler, is to use the new Ref<> class of the devel branch. I refer to the doc for the details of this approach.