This error occurs because a borrow in a movable coroutine persists across a
yield point.
Erroneous code example:
#![allow(unused)]fnmain() {
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]use std::ops::Coroutine;
use std::pin::Pin;
letmut b = #[coroutine] || {
let a = &String::new(); // <-- This borrow...yield (); // ...is still in scope here, when the yield occurs.println!("{}", a);
};
Pin::new(&mut b).resume(());
}
ⓘ
Coroutines may be either unmarked, or marked with static. If it is unmarked,
then the coroutine is considered "movable". At present, it is not permitted to
have a yield in a movable coroutine that occurs while a borrow is still in
scope. To resolve this error, the coroutine may be marked static:
#![allow(unused)]fnmain() {
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]use std::ops::Coroutine;
use std::pin::Pin;
letmut b = #[coroutine]static || { // <-- note the static keywordlet a = &String::from("hello, world");
yield ();
println!("{}", a);
};
letmut b = std::pin::pin!(b);
b.as_mut().resume(());
}
If the coroutine must remain movable, for example to be used as Unpin
without pinning it on the stack or in an allocation, we can alternatively
resolve the previous example by removing the borrow and just storing the
type by value:
#![allow(unused)]fnmain() {
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]use std::ops::Coroutine;
use std::pin::Pin;
letmut b = #[coroutine] || {
let a = String::from("hello, world");
yield ();
println!("{}", a);
};
Pin::new(&mut b).resume(());
}
This is a very simple case, of course. In more complex cases, we may
wish to have more than one reference to the value that was borrowed --
in those cases, something like the Rc or Arc types may be useful.
This error also frequently arises with iteration:
#![allow(unused)]fnmain() {
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]use std::ops::Coroutine;
use std::pin::Pin;
letmut b = #[coroutine] || {
let v = vec![1,2,3];
for &x in &v { // <-- borrow of `v` is still in scope...yield x; // ...when this yield occurs.
}
};
Pin::new(&mut b).resume(());
}
ⓘ
Such cases can sometimes be resolved by iterating "by value" (or using
into_iter()) to avoid borrowing:
#![allow(unused)]fnmain() {
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]use std::ops::Coroutine;
use std::pin::Pin;
letmut b = #[coroutine] || {
let v = vec![1,2,3];
for x in v { // <-- Take ownership of the values instead!yield x; // <-- Now yield is OK.
}
};
Pin::new(&mut b).resume(());
}
If taking ownership is not an option, using indices can work too:
#![allow(unused)]fnmain() {
#![feature(coroutines, coroutine_trait, stmt_expr_attributes)]use std::ops::Coroutine;
use std::pin::Pin;
letmut b = #[coroutine] || {
let v = vec![1,2,3];
let len = v.len(); // (*)for i in0..len {
let x = v[i]; // (*)yield x; // <-- Now yield is OK.
}
};
Pin::new(&mut b).resume(());
// (*) -- Unfortunately, these temporaries are currently required.// See <https://github.com/rust-lang/rust/issues/43122>.}