Send 추정

일부 async fn 상태 시스템은 스레드를 통해 전송되는 것이 안전하지만 다른 것들은 그렇지 않습니다. async fn FutureSend 인지 여부가 결정되는 것은 non-Send 타입이 .await 지점에서 유지되는지 여부에 의합니다. 컴파일러는 객체가 .await에 걸쳐있을 때 Send에 근사하기 위해 최선을 다합니다. 그러나 이 분석은 오늘날 여러 곳에서 너무 보수적입니다.

예를 들어, 단순한 non-Send타입, 아마도 Rc가 들어 있는 타입을 생각해 보세요 :


#![allow(unused)]
fn main() {
use std::rc::Rc;

#[derive(Default)]
struct NotSend(Rc<()>);
}

NotSend 타입의 변수는 async fn 들에서 async fn에 의해 리턴 된 결과 Future타입 값이 Send 여야 하는 경우에도 일시적으로 나타날 수 있습니다 :

use std::rc::Rc;
#[derive(Default)]
struct NotSend(Rc<()>);
async fn bar() {}
async fn foo() {
    NotSend::default();
    bar().await;
}

fn require_send(_: impl Send) {}

fn main() {
    require_send(foo());
}

하지만 만약 우리가 fooNotSend를 변수에 담도록 수정하면 이 예제는 더 이상 컴파일 되지 않습니다 :

use std::rc::Rc;
#[derive(Default)]
struct NotSend(Rc<()>);
async fn bar() {}
async fn foo() {
    let x = NotSend::default();
    bar().await;
}
fn require_send(_: impl Send) {}
fn main() {
   require_send(foo());
}

#![allow(unused)]
fn main() {
error[E0277]: `std::rc::Rc<()>` cannot be sent between threads safely
  --> src/main.rs:15:5
   |
15 |     require_send(foo());
   |     ^^^^^^^^^^^^ `std::rc::Rc<()>` cannot be sent between threads safely
   |
   = help: within `impl std::future::Future`, the trait `std::marker::Send` is not implemented for `std::rc::Rc<()>`
   = note: required because it appears within the type `NotSend`
   = note: required because it appears within the type `{NotSend, impl std::future::Future, ()}`
   = note: required because it appears within the type `[static generator@src/main.rs:7:16: 10:2 {NotSend, impl std::future::Future, ()}]`
   = note: required because it appears within the type `std::future::GenFuture<[static generator@src/main.rs:7:16: 10:2 {NotSend, impl std::future::Future, ()}]>`
   = note: required because it appears within the type `impl std::future::Future`
   = note: required because it appears within the type `impl std::future::Future`
note: required by `require_send`
  --> src/main.rs:12:1
   |
12 | fn require_send(_: impl Send) {}
   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error

For more information about this error, try `rustc --explain E0277`.
}

이 오류는 정확 합니다. x를 변수에 저장하면 .await 이후까지 삭제되지 않습니다. 그 시점에서 async fn은 다른 스레드에서 실행될 수 있습니다. RcSend가 아니기 때문에 스레드를 가로 질러 이동하면 매우 안 좋은 것이죠. 이것에 대한 간단한 해결책은 .await 이전에 Rcdrop 하는 것입니다. 하지만 불행히도 이 방법은 오늘날에는 작동하지 않습니다.

이 문제를 성공적으로 해결하려면 non-Send의 변수를 캡슐화하는 블록 스코프를 도입해야 할 수도 있습니다. 이것은 컴파일러에게는 이러한 변수가 .await 지점을 가로질러 살아 있지 않다고 말해주므로 더 쉬워집니다 :

use std::rc::Rc;
#[derive(Default)]
struct NotSend(Rc<()>);
async fn bar() {}
async fn foo() {
    {
        let x = NotSend::default();
    }
    bar().await;
}
fn require_send(_: impl Send) {}
fn main() {
   require_send(foo());
}