Вот не понимаю почему, такой код:
pub fn run_blocks(cnc: Rc<RefCell<Controller>>, blocks: Blocks) -> impl Future<Item=(), Error=Error> {
let cnc_start = cnc.clone();
let cnc_load = cnc.clone();
load(cnc, blocks)
.and_then(move |(blocks, runtime)| cnc_start.borrow_mut().start().map(|_| (blocks, runtime)))
.and_then(move |(blocks, runtime)| loop_fn((blocks, runtime), move |(blocks, runtime)| {
let cnc_load_clone = cnc_load.clone();
Delay::new(Instant::now() + runtime - Duration::from_secs(1))
.map_err(|err| panic!("timer failed; err={:?}", err))
.and_then(move |_| load(cnc_load_clone, blocks).map(|(blocks, runtime)| {
if blocks.is_empty() {
Loop::Break((blocks, runtime))
} else {
Loop::Continue( (blocks, runtime) )
}
}))
}).map(|_| () )
)
}
вызывает ошибку:
error[E0597]: `cnc_start` does not live long enough
--> plc/src/lib.rs:78:40
|
78 | .and_then(move |(blocks, runtime)| cnc_start.borrow_mut().start().map(|_| (blocks, runtime)))
| ^^^^^^^^^ borrowed value does not live long enough - `cnc_start` dropped here while still borrowed
|
= note: values in a scope are dropped in the opposite order they are created
and_then - принимает FnOnce и в замыкание передано значение. Что не так?
нарывался на подобное когда пытался максимально близко переписывать на Rust С код.
попробуй заинлайнить
убрав
let cnc_start = cnc.clone() совсем
load(cnc, blocks)
.and_then(move |(blocks, runtime)| cnc.clone().borrow_mut().start().map(|_| (blocks, runtime)))
попробовал, та же самая ошибка.
код для экспериментов
Похоже несовершенство borrow checker’а в Rust’2015.
Разбивка цепочки вызовов фиксит проблему
let res = cnc_start
.borrow_mut()
.start();
res
.map(move |_| (blocks, runtime))
Еще раз те-же грабли:
use std::sync::{Arc, Mutex};
fn main() {
let a = Arc::new(Mutex::new(String::new()));
let b = Arc::new(Mutex::new(String::new()));
match a.lock().and_then(|a| b.lock().map(|b| (a, b))) {
Ok((a, b)) => {
println!("a = {}, b = {}", a, b);
}
Err(err) => {
println!("{}", err);
}
}
}
Playground
Так работает:
let tuple = a.lock().and_then(|a| b.lock().map(|b| (a, b)));
match tuple {
//...
}
Похоже на баг в NLL. Стоит наверно создать ишью.
Хм, будет работать если просто добавить точку с запятой после match.
Баг воспроизводится с одним Arc:
let a = Arc::new(Mutex::new(String::new()));
match a.lock() {
Ok(a) => {}
Err(_) => {}
}
Playground
И кстати, в сообщении об ошибке компилятор пишет:
note: The temporary is part of an expression at the end of a block. Consider adding semicolon after the expression so its temporaries are dropped sooner, before the local variables declared by the block are dropped.
Так что это не баг, а фича. 
Судя по всему match создает неявную временную переменную, которая дропается после переменных объявленных в его блоке.
Т.е. происходит что-то вроде этого:
let t;
let a = Arc::new(Mutex::new(String::new()));
t = a.lock();
match t {_ => {}}
Получим почти тоже самое сообщение об ошибке:
error[E0597]: `a` does not live long enough
--> src/lib.rs:6:9
|
6 | t = a.lock();
| ^ borrowed value does not live long enough
7 | match t {_ => {}}
8 | }
| -
| |
| `a` dropped here while still borrowed
Не знаю что мешает создавать временную переменную как все нормальные люди:
let a = Arc::new(Mutex::new(String::new()));
let t = a.lock();
match t {_ => {}}
Мне не удаётся воспроизвести этот эффект с простыми ссылками. Работает (в смысле выдает ошибку) только с Mutex и RefCell.
И даже с самодельным RefCell (все скопировано из std::cell) ошибка не воспроизводится. А если раскоментировать стандартый RefCell Rust начинает ругаться. 
Это магия какая-то? Или я что-то делаю не так?
Playground
Нашел что это за магия. Все дело в типаже Drop. Если его заимплементить для MyBorrowRef из примера выше, то самодельный RefCell начинает работать также как стандартный.
Вот пример воспроизводящий эту багофичу без всяких Mutex-ов и RefCell-ов:
struct MyRefHaveDrop<'x>(&'x usize);
impl Drop for MyRefHaveDrop<'_> {
#[inline]
fn drop(&mut self) {}
}
fn main() {
let data = 451;
match MyRefHaveDrop(&data) { _ => {} }
}
Playground
Структура со ссылкой, для которой реализован Drop, уничтожается позже чем данные на которые она ссылается.
Такой же эффект наблюдается если заменить match на if let.
P.S.: По-моему это очень плохая фича. Она позволяет ломать компиляцию зависимых крейтов. Добавил реализацию Drop к публичной структуре и всё.
Если такое поведение match нельзя исправить, то компилятору стоило бы выдавать ошибку для всех типов независимо от наличия у них типажа Drop.
P.P.S.: Хотя если подумать, то Drop-ом можно сломать вообще все что угодно.