2015-03-24 4 views
3

У меня есть короткий пример увеличения вектора путем деления и покорения. Очень простой, я просто не могу получить правильные сроки жизни. Я уверен, что это связано с временем жизни аргумента &'s mut и возвратом жизни TaskResult<'s>, но я не уверен, как заставить его работать.Пожизненные проблемы передачи & mut для функционирования и возврата закрытия

code on the playpen

fn main() { 
    let mut data = vec![1,2,3,4,5,6,7,8,9]; 
    let t = inc_vec(data.as_mut_slice()); 
} 

pub type MyClosure<'s> = FnMut() -> TaskResult<'s> + Send + 's; 

pub enum TaskResult<'s> { 
    Done(usize), 
    Fork(Vec<Box<MyClosure<'s>>>), 
} 

fn inc_vec<'s>(data: &'s mut [usize]) -> TaskResult { 
    if data.len() <= 4 { 
     inc_vec_direct(data) 
    } else { 
     inc_vec_fork(data) 
    } 
} 

fn inc_vec_fork<'s>(data: &'s mut [usize]) -> TaskResult<'s> { 
    let mid = data.len()/2; 
    let (l,r) = data.split_at_mut(mid); 

    let task_l: Box<MyClosure<'s>> = Box::new(move || { 
     inc_vec(l) 
    }); 
    let task_r: Box<MyClosure<'s>> = Box::new(move || { 
     inc_vec(r) 
    }); 

    TaskResult::Fork(vec![task_l, task_r]) 
} 

fn inc_vec_direct(data: &mut [usize]) -> TaskResult { 
    for d in data { 
     *d += 1; 
    } 
    TaskResult::Done(1) 
} 

И это дает мне следующую ошибку (усеченный так же ошибка производится дважды, один раз для task_l и один раз для task_r):

src/main.rs:26:17: 26:18 error: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements 
src/main.rs:26   inc_vec(l) 
          ^
src/main.rs:25:55: 27:6 note: first, the lifetime cannot outlive the lifetime as defined on the block at 25:54... 
src/main.rs:25  let task_l: Box<MyClosure<'s>> = Box::new(move || { 
src/main.rs:26   inc_vec(l) 
src/main.rs:27  }); 
src/main.rs:26:17: 26:18 note: ...so that closure can access `l` 
src/main.rs:26   inc_vec(l) 
          ^
src/main.rs:21:62: 33:2 note: but, the lifetime must be valid for the lifetime 's as defined on the block at 21:61... 
src/main.rs:21 fn inc_vec_fork<'s>(data: &'s mut [usize]) -> TaskResult<'s> { 
src/main.rs:22  let mid = data.len()/2; 
src/main.rs:23  let (l,r) = data.split_at_mut(mid); 
src/main.rs:24 
src/main.rs:25  let task_l: Box<MyClosure<'s>> = Box::new(move || { 
src/main.rs:26   inc_vec(l) 
       ... 
src/main.rs:25:38: 27:7 note: ...so that trait type parameters matches those specified on the impl (expected `TaskResult<'_>`, found `TaskResult<'s>`) 
src/main.rs:25  let task_l: Box<MyClosure<'s>> = Box::new(move || { 
src/main.rs:26   inc_vec(l) 
src/main.rs:27  }); 

Там должно быть просто исправить это. Все, что я хочу сказать, это то, что я возвращаю вектор замыканий, которые имеют изменяемые ссылки на части входного среза. Я предполагаю, что я должен отметить время жизни закрытия короче, чем время жизни среза данных, просто не уверен, как это сделать.

+0

Здесь нет простого исправления, потому что по сути небезопасно передавать ссылки на потоки, поскольку родительский поток, которому принадлежит эта ссылка, может завершаться перед другими потоками, делая недействительным то, на что указывают ссылки в дочерних элементах. Вам придется немного изменить свой дизайн. Посмотрите на http://stackoverflow.com/questions/26477757/how-do-you-send-slices-of-a-vec-to-a-task-in-rust для возможных обходных решений. –

+2

@ FilipeGonçalves это не совсем правильно, потому что есть механизм, который гарантирует, что ссылка не переживет родительский стековый фрейм (см. ['Thread :: scoped'] (http://doc.rust-lang.org/ std/thread/fn.scoped.html)), даже если он передан в другой поток. В этом конкретном случае даже нет потоков. –

ответ

2

Вы можете получить свой пример, чтобы скомпилировать и запустить, если изменить одну строку:

pub type MyClosure<'s> = FnOnce() -> TaskResult<'s> + Send + 's; 
//      ^~~~~~ 

Я все еще продумывая, как объяснить это, хотя!

Это код, из которого я начинал. Я сделал несколько упрощений, чтобы начать работу, в основном вокруг удаления ссылок на всю жизнь, где они не нужны. Срок службы означает, что fn(foo: &T) -> &U совпадает с fn<'a>(foo: &'a T) -> &'a U, но не идентичен как fn<'a>(foo: &'a T) -> &U.

fn main() { 
    let mut data = vec![1,2,3,4,5,6,7,8,9]; 
    let t = inc_vec(data.as_mut_slice()); 
} 

pub type MyClosure<'s> = FnMut() -> TaskResult<'s> + Send + 's; 

pub enum TaskResult<'s> { 
    Done(usize), 
    Fork(Vec<Box<MyClosure<'s>>>), 
} 

fn inc_vec(data: &mut [usize]) -> TaskResult { 
    if data.len() <= 4 { 
     inc_vec_direct(data) 
    } else { 
     inc_vec_fork(data) 
    } 
} 

fn inc_vec_fork(data: &mut [usize]) -> TaskResult { 
    let mid = data.len()/2; 
    let (l, r) = data.split_at_mut(mid); 

    let task_l: Box<MyClosure> = Box::new(move || inc_vec(l)); 
    let task_r: Box<MyClosure> = Box::new(move || inc_vec(r)); 

    TaskResult::Fork(vec![task_l, task_r]) 
} 

fn inc_vec_direct(data: &mut [usize]) -> TaskResult { 
    for d in data { *d += 1; } 
    TaskResult::Done(1) 
} 

Главным образом, я получил в результате, изменяя Замыкание только smidge:

let task_l: Box<MyClosure> = Box::new(move || { let a = l; inc_vec(a)}); 

Какой должен быть тот же код. Однако это ошибка:

error: cannot move out of captured outer variable in an `FnMut` closure 
    let task_l: Box<MyClosure> = Box::new(move || { let a = l; inc_vec(a)}); 
                  ^
note: attempting to move value to here 
    let task_l: Box<MyClosure> = Box::new(move || { let a = l; inc_vec(a)}); 
                 ^
help: to prevent the move, use `ref a` or `ref mut a` to capture value by reference 

который привел меня, чтобы попробовать различные Fn* черты, с FnOnce работает. I думаю, что решение сводится к тому, что Rust запрещает сглаживание изменчивых ссылок (a.k.a., вы не можете указывать на одну и ту же изменчивую вещь дважды). Если у вас есть FnMut или Fn, вы можете вызвать его несколько раз, что даст возможность создавать псевдонимы. Было бы неплохо, если бы сообщение об ошибке содержало что-либо о изменчивости!

А FnOnce гарантированно будет вызываться только один раз, что предотвращает эту возможность сглаживания.

Я думаю, вы могли бы подать 1 или 2 ошибки из этого:

  1. Удивительно, что изменения сообщение об ошибке на основе, если есть let или нет.
  2. Было бы неплохо упомянуть изменчивость как причину, по которой переменная не может быть перенесена в закрытие.
+1

Вы не можете использовать значение типа '& mut [usize]' путем заимствования внутри закрытия. Его можно использовать, перемещая его. Для его перемещения требуется 'FnOnce'. – bluss

+0

[Комментарий к редактированию вопроса, а также этот ответ.] Используйте код '' в коде Rust, он делает уродливые '// '' форматирование хаков в конце строки ненужной. –

+0

@Shepmaster: да, это все еще не так хорошо, как на самом деле имеет дело с токенами жизни должным образом, но тот факт, что он не выделяет его за пределы перерывов, является улучшением. –