Tentukan umur penutupan Rust
Saya membuat eksekutor / reaktor ketika menemukan ini masalah seumur hidup. Itu tidak terkait dengan async / Future dan dapat direproduksi tanpa gula asinkron.
use std::future::Future;
struct Runtime;
fn start_with_runtime<C, F>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> F,
F: Future
{
let rt = Runtime;
let _future = closure(&rt);
// block_on(future);
}
async fn async_main(_rt: &Runtime) {
// I can use _rt to do async stuff here
}
fn main() {
start_with_runtime(|rt| { async_main(rt) });
}
Saya ingin start_with_runtime()menjalankan masa depan dan memberikan referensi Runtime async sebagai parameter.
Itu tidak mengkompilasi:
error: lifetime may not live long enough
--> src/main.rs:17:31
|
17 | start_with_runtime(|rt| { async_main(rt) });
| --- ^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is impl std::future::Future
| has type `&'1 Runtime`
Saya pikir masalah ini tampaknya karena bagaimana karat menyimpulkan masa hidup untuk penutupan:
https://github.com/rust-lang/rust/issues/58052 :
fn main() {
let f = |x: &i32| x;
let i = &3;
let j = f(i);
}
Tidak mengkompilasi juga:
error: lifetime may not live long enough
--> src/main.rs:2:23
|
2 | let f = |x: &i32| x;
| - - ^ returning this value requires that `'1` must outlive `'2`
| | |
| | return type of closure is &'2 i32
| let's call the lifetime of this reference `'1`
Sepertinya tanda tangan penutupan saya disimpulkan sebagai |&'a Runtime| -> impl Future + 'bkesalahan seumur hidup. Saya merasa bahwa memberikan tanda tangan yang diharapkan dengan benar untuk penutupan akan membantu, tetapi bagaimana cara memberikan tanda tangan yang benar start_with_runtime?
fn start_with_runtime<C>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> (impl Future + 'a),
Tidak berfungsi karena impl Traittidak diperbolehkan disini.
fn start_with_runtime<C,F>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> F,
F: Future + 'a
Tidak berfungsi dengan baik karena 'atidak dikenal di luar ekspresi HRTB.
Ini berfungsi jika saya tahu tipenya:
struct MyType<'a> {
_rt: &'a Runtime
}
fn start_with_runtime<C>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> MyType<'a>,
Ini agak menyedihkan ketika Anda telah memikirkannya sepanjang masa, tetapi bahasa tidak menyediakan cara untuk mengungkapkannya. Mungkin ada trik karat untuk membuat ini berhasil?
Jawaban
Tampaknya ada dua pertanyaan berbeda dalam hal ini: dapatkah hubungan yang diperlukan diekspresikan dalam sintaks Rust dan apakah akan bekerja dengan inferensi tipe penutupan atau tidak.
Mari kita mulai dengan yang pertama. Anda benar bahwa ini tidak dapat diungkapkan hanya dengan whereklausa. Untuk mengungkapkan hal ini perlu menambahkan sifat penolong
trait BorrowingFn<'a> {
type Fut: std::future::Future<Output = Something> + 'a;
fn call(self, arg: &'a Runtime) -> Self::Fut;
}
yang memungkinkan ikatan yang kita butuhkan untuk ditulis
C: for<'a> BorrowingFn<'a>,
dan memberikan implementasi menyeluruh dari sifat ini untuk semua fungsi yang berlaku
impl<'a, Fu: 'a, F> BorrowingFn<'a> for F
where
F: FnOnce(&'a Runtime) -> Fu,
Fu: std::future::Future<Output = ()> + 'a,
{
type Fut = Fu;
fn call(self, rt: &'a Runtime) -> Fu {
self(rt)
}
}
( taman bermain )
Oke, jadi ini bekerja dengan fungsi async, tapi apakah itu bekerja dengan closure yang membutuhkan inferensi tipe? Sayangnya, jawabannya adalah "tidak"
error: implementation of `BorrowingFn` is not general enough
--> src/main.rs:33:5
|
5 | / trait BorrowingFn<'a> {
6 | | type Fut: std::future::Future<Output = ()> + 'a;
7 | | fn call(self, arg: &'a Runtime) -> Self::Fut;
8 | | }
| |_- trait `BorrowingFn` defined here
...
33 | start_with_runtime(|rt| async_main(rt)); // however, it does not work with closure type inference :-(
| ^^^^^^^^^^^^^^^^^^ implementation of `BorrowingFn` is not general enough
|
= note: `[closure@src/main.rs:33:24: 33:43]` must implement `BorrowingFn<'0>`, for any lifetime `'0`...
= note: ...but `[closure@src/main.rs:33:24: 33:43]` actually implements `BorrowingFn<'1>`, for some specific lifetime `'1`
Ini dilacak dalam rust-lang / rust # 70263 . Kompilator belum cukup pintar untuk memperhatikan bahwa penutupan ini membutuhkan tipe dengan peringkat lebih tinggi. Untuk bersenang-senang saya mencoba mengompilasi dengan -Z chalkdi Nightly, tetapi belum siap untuk ini (kesalahan kompiler internal).
Maaf, ini adalah batasan dalam bahasa. Anda hanya dapat menentukan masa pakai pada jenis beton. Salah satu solusinya adalah dengan menggunakan tipe objek sifat.
fn start_with_runtime<C, F, T>(closure: C)
where
C: for<'a> FnOnce(&'a Runtime) -> Pin<Box<dyn Future<Item = T> + Send + 'a>>,
{
let rt = Runtime;
let _future = closure(&rt);
// block_on(future);
}