Send 추정
일부 async fn 상태 시스템은 스레드를 통해 전송되는 것이 안전하지만
다른 것들은 그렇지 않습니다. async fn Future 가 Send 인지 여부가 결정되는 것은
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()); }
하지만 만약 우리가 foo가 NotSend를 변수에 담도록 수정하면 이 예제는 더 이상 컴파일 되지 않습니다 :
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은
다른 스레드에서 실행될 수 있습니다. Rc는 Send가 아니기 때문에 스레드를 가로 질러 이동하면
매우 안 좋은 것이죠. 이것에 대한 간단한 해결책은 .await 이전에 Rc를 drop 하는 것입니다.
하지만 불행히도 이 방법은 오늘날에는 작동하지 않습니다.
이 문제를 성공적으로 해결하려면 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()); }