So, this doesn't work:
use std::comm;
#[deriving(Show)]
struct St { v: u8 }
fn main() {
let mut foo:Vec<St> = Vec::new();
for i in range(0u8, 10) {
foo.push(St { v: i });
}
{
let mut foo_slice = foo.as_mut_slice();
let (f1, f2) = foo_slice.split_at_mut(5);
let (sl, rx):(Sender<Option<&mut [St]>>, Receiver<Option<&mut [St]>>) = comm::channel();
let (sx, rl):(Sender<bool>, Receiver<bool>) = comm::channel();
spawn(proc() {
loop {
let v = rx.recv();
match v {
Some(v) => {
v[0].v = 100u8;
sx.send(true);
},
None => {
sx.send(false);
break;
}
}
}
});
sl.send(Some(f1));
sl.send(Some(f2));
sl.send(None);
println!("{}", rl.recv());
println!("{}", rl.recv());
println!("{}", rl.recv());
}
println!("{}", foo);
}
...because:
sl.send(Some(f1));
sl.send(Some(f2));
sl.send(None);
Infers that the variables f1 and f2 must be 'static, because the task may outlive the function it is running in. Which in turn means that foo must be 'static, and not 'a, which is the lifetime of main().
Thus the somewhat odd error:
<anon>:14:27: 14:30 error: `foo` does not live long enough
<anon>:14 let mut foo_slice = foo.as_mut_slice();
^~~
note: reference must be valid for the static lifetime...
<anon>:6:11: 46:2 note: ...but borrowed value is only valid for the block at 6:10
<anon>:6 fn main() {
<anon>:7
<anon>:8 let mut foo:Vec<St> = Vec::new();
<anon>:9 for i in range(0u8, 10) {
<anon>:10 foo.push(St { v: i });
<anon>:11 }
So, to fix this I thought that using Box <Vec<Foo>> might be the solution, but even then the slices created will have a local lifetime.
I could use unsafe code to transmute the lifetime (this does actually work), but is there a way to safely do the same thing?
playpen: http://is.gd/WQBdSB
Rust prevents you from having mutable access to the same value from within multiple tasks, because that leads to data races. Specifically, a task can't have borrowed pointers (incl. slices) to a value that is owned by another task.
To allow multiple tasks to access the same object, you should use
Arc<T>
. To provide mutable access to the object, put aRefCell<T>
in thatArc
:Arc<RefCell<T>>
. As for thatT
, you can't use a slice type, as I just explained. I suggest you create 2 differentArc<RefCell<Vec<St>>>
objects, send a clone of anArc<RefCell<Vec<St>>>
on the channel and join theVec
s when the tasks have done their job.In general, when doing parallel algorithms, you should avoid mutating shared state. This leads to poor performance, because the system needs to invalidate memory caches across cores. If possible, consider having the task allocate and hold on to its result until it's complete, and send the complete result over a channel, rather than a mere
bool
.EDIT
We can reformulate your initial program in terms of ownership to understand why it's not sound. The stack frame for the call to
main
ownsfoo
, theVec
.foo_slice
,f1
andf2
borrow thatVec
. You spawn a task. That task may outlive the call frame formain
, and even outlive the task that spawned it. Therefore, it is illegal to send references to values that are constrained to a stack frame. This is why borrowed pointers, with the exception of&'static T
, don't fulfillSend
.Boxing the
Vec
changes nothing, because the stack frame still owns theBox
, so returning from the function will drop the box and its contents.The compiler cannot verify that the task won't outlive the owner of the values you send references to to the task. If you are sure that the task will terminate before the references you give it become invalid, you can use
transmute
to cheat on the lifetime, but this is unsafe.