How do you send slices of a Vec to a task in rust?

2020-02-14 09:59发布

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
1条回答
ゆ 、 Hurt°
2楼-- · 2020-02-14 10:42

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 a RefCell<T> in that Arc: Arc<RefCell<T>>. As for that T, you can't use a slice type, as I just explained. I suggest you create 2 different Arc<RefCell<Vec<St>>> objects, send a clone of an Arc<RefCell<Vec<St>>> on the channel and join the Vecs 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 owns foo, the Vec. foo_slice, f1 and f2 borrow that Vec. You spawn a task. That task may outlive the call frame for main, 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 fulfill Send.

Boxing the Vec changes nothing, because the stack frame still owns the Box, 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.

查看更多
登录 后发表回答