Future와 async·await 문서의 검증 코드다.
//! 36 문서의 컴파일러 주장을 확인한다: 버린 퓨처 경고, async 밖 .await(E0728),
//! .await 너머 Rc가 퓨처를 Send가 아니게 만드는 것, !Unpin 퓨처를 Pin 밖으로 꺼내지 못하는 것,
//! 그리고 async 관련 clippy 린트가 기본으로 켜져 있는지.
//! 의존성: 없음 (표준 라이브러리만). rustc와 clippy-driver를 PATH에서 부른다.
//! 실행: python3 "_시스템/Rust 계획/rs_check.py" "Rust/3.개념집/36_futures-async-await_verify.rs"
use std::process::Command;
fn compile(tool: &str, extra: &[&str], code: &str, tag: &str) -> String {
let dir = std::env::temp_dir().join(format!("vr36_{}_{}", std::process::id(), tag));
std::fs::create_dir_all(&dir).unwrap();
let file = dir.join("snippet.rs");
std::fs::write(&file, code).unwrap();
let out = Command::new(tool)
.args(["--edition", "2024", "--crate-type=lib", "--emit=metadata"])
.args(extra)
.arg("--out-dir")
.arg(&dir)
.arg(&file)
.output()
.expect("컴파일러 실행");
String::from_utf8_lossy(&out.stderr).into_owned()
}
fn rustc(code: &str, tag: &str) -> String {
compile("rustc", &[], code, tag)
}
const REQUIRE_SEND: &str = r#"
use std::rc::Rc;
async fn tick() {}
fn require_send<T: Send>(_: T) {}
"#;
fn main() {
// 1. 퓨처를 만들고 버리면 경고: 퓨처는 await하거나 poll해야 돈다
let e = rustc(
"async fn work() -> u32 { 1 }\npub fn f() { work(); }\n",
"unused",
);
println!(
"--- 버린 퓨처 ---\n{}",
e.lines().take(8).collect::<Vec<_>>().join("\n")
);
assert!(e.contains("unused implementer of `Future` that must be used"));
assert!(e.contains("futures do nothing unless you `.await` or poll them"));
// 2. async가 아닌 함수에서 .await → E0728
let e = rustc(
"async fn work() -> u32 { 1 }\npub fn f() -> u32 { work().await }\n",
"e0728",
);
assert!(e.contains("error[E0728]"), "{e}");
println!("--- async 밖 .await ---\n{}", e.lines().next().unwrap());
// 3. Rc를 .await 너머로 들고 가면 퓨처가 Send가 아니다
let bad = format!(
"{REQUIRE_SEND}pub fn f() {{ require_send(async {{ let r = Rc::new(1); tick().await; println!(\"{{r}}\"); }}); }}\n"
);
let e = rustc(&bad, "rc_bad");
println!(
"--- Rc를 들고 .await ---\n{}",
e.lines().take(3).collect::<Vec<_>>().join("\n")
);
assert!(
e.contains("future cannot be sent between threads safely"),
"{e}"
);
assert!(e.contains("is not `Send`"), "{e}");
// 3-1. .await 전에 Rc의 범위를 끝내면 통과
let ok = format!(
"{REQUIRE_SEND}pub fn f() {{ require_send(async {{ {{ let r = Rc::new(1); println!(\"{{r}}\"); }} tick().await; }}); }}\n"
);
let e = rustc(&ok, "rc_ok");
assert!(!e.contains("error"), "{e}");
// 4. !Unpin인 async 퓨처는 Pin<&mut _>에서 &mut로 꺼낼 수 없다 (E0277, Unpin 없음)
let e = rustc(
"use std::pin::Pin;\nuse std::future::Future;\n\
pub fn f<F: Future>(p: Pin<&mut F>) -> &mut F { Pin::get_mut(p) }\n",
"unpin",
);
assert!(e.contains("error[E0277]") && e.contains("Unpin"), "{e}");
println!("--- Pin::get_mut ---\n{}", e.lines().next().unwrap());
// 5. clippy: 어떤 린트가 기본으로 켜져 있나
let lint_src = r#"
use std::sync::Mutex;
async fn tick() {}
pub async fn holds(m: &Mutex<i32>) { let g = m.lock().unwrap(); tick().await; drop(g); }
pub fn ignore() { let _ = tick(); }
pub async fn no_await() -> i32 { 1 }
pub fn not_send() -> impl std::future::Future<Output = ()> {
async { let r = std::rc::Rc::new(1); tick().await; drop(r); }
}
"#;
let d = compile("clippy-driver", &[], lint_src, "clippy_default");
let p = compile(
"clippy-driver",
&["-W", "clippy::pedantic"],
lint_src,
"clippy_ped",
);
let n = compile(
"clippy-driver",
&["-W", "clippy::nursery"],
lint_src,
"clippy_nur",
);
let on_by_default = |l: &str| d.contains(&format!("`#[warn(clippy::{l})]` on by default"));
assert!(on_by_default("await_holding_lock"));
assert!(on_by_default("let_underscore_future"));
assert!(
!d.contains("unused_async")
&& p.contains("clippy::unused-async` implied by `-W clippy::pedantic`")
);
assert!(
!d.contains("future_not_send")
&& n.contains("clippy::future-not-send` implied by `-W clippy::nursery`")
);
println!(
"--- clippy ---\n기본: await_holding_lock, let_underscore_future / pedantic: unused_async / nursery: future_not_send"
);
println!("ALL CHECKS PASSED");
}