Rust 클로저 수명 지정

Aug 21 2020

나는 이것이 평생의 문제임을 발견하면서 집행자 / 반응자를 만들고 있었다. 비동기 / 미래와 관련이 없으며 비동기 설탕없이 재현 할 수 있습니다.

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) });
}

나는 싶습니다 start_with_runtime()미래를 실행하고 매개 변수로 비동기 런타임 참조를 제공 할 수 있습니다.

컴파일되지 않습니다.

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`

이 문제는 녹이 클로저의 수명을 유추하는 방법 때문인 것 같습니다.

https://github.com/rust-lang/rust/issues/58052 :

fn main() {
    let f = |x: &i32| x;
    let i = &3;
    let j = f(i);
}

다음 중 하나를 컴파일하지 않습니다.

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`

내 클로저 서명 |&'a Runtime| -> impl Future + 'b이 수명 오류 로 추론 된 것 같습니다 . 마감에 대한 올바른 예상 서명이 도움이 될 것이라고 생각하지만 올바른 서명을 start_with_runtime어떻게 제공 합니까?

fn start_with_runtime<C>(closure: C)
where
    C: for<'a> FnOnce(&'a Runtime) -> (impl Future + 'a),

impl Trait여기에서 허용되지 않기 때문에 작동 하지 않습니다.

fn start_with_runtime<C,F>(closure: C)
where
    C: for<'a> FnOnce(&'a Runtime) -> F,
    F: Future + 'a

'aHRTB 표현식 외부에서 알려지지 않았기 때문에 잘 작동 하지 않습니다.

유형을 알고 있으면 작동합니다.


struct MyType<'a> {
    _rt: &'a Runtime
} 
fn start_with_runtime<C>(closure: C)
where
    C: for<'a> FnOnce(&'a Runtime) -> MyType<'a>,

당신이 평생 동안 생각했지만 언어가 이것을 표현하는 방법을 제공하지 않을 때 이것은 다소 슬픈 일입니다. 이 작업을 수행하는 데 녹슬지 않는 트릭이 있습니까?

답변

1 Tanriol Aug 24 2020 at 16:22

여기에는 두 가지 다른 질문이있는 것 같습니다. 필요한 관계를 Rust 구문으로 표현할 수 있고 클로저 유형 추론과 함께 작동할지 여부입니다.

첫 번째부터 시작하겠습니다. 이것이 where절로 만 표현 될 수 없다는 것이 맞습니다 . 이것을 표현하려면 도우미 특성을 추가해야합니다.

trait BorrowingFn<'a> {
    type Fut: std::future::Future<Output = Something> + 'a;
    fn call(self, arg: &'a Runtime) -> Self::Fut;
}

경계를 다음과 같이 작성해야합니다.

    C: for<'a> BorrowingFn<'a>,

적용 가능한 모든 기능에 대해이 특성의 포괄적 인 구현을 제공합니다.

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)
    }
}

( 놀이터 )

좋아, 비동기 함수와 함께 작동하지만 유형 추론이 필요한 클로저와 함께 작동합니까? 불행히도 대답은 "아니오"입니다.

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`

이것은 rust-lang / rust # 70263 에서 추적되고 있습니다. 컴파일러는 아직이 클로저에 더 높은 등급의 유형이 필요하다는 것을 알아 차릴만큼 똑똑하지 않습니다. 재미 -Z chalk로 Nightly에서 컴파일을 시도했지만 아직 준비되지 않았습니다 (내부 컴파일러 오류).

AliceRyhl Aug 23 2020 at 15:08

죄송합니다. 이것은 언어 제한입니다. 구체적인 유형에 대해서만 수명을 지정할 수 있습니다. 한 가지 해결 방법은 특성 개체 유형을 사용하는 것입니다.

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); 
}