tokio 런타임과 태스크 문서의 검증 코드다.

//! tokio::spawn의 경계(F: Future + Send + 'static, F::Output: Send + 'static)와 같은 함수를
//! 표준 라이브러리만으로 만들어, 지역 변수를 빌리는 async 블록이 왜 거부되는지 오류 코드로 확인한다.
//! 의존성: 없음 (표준 라이브러리만). rustc를 PATH에서 부른다.
//! 실행: python3 "_시스템/Rust 계획/rs_check.py" "Rust/3.개념집/37_tokio-runtime-tasks_verify.rs"

fn compile_errors(code: &str, tag: &str) -> String {
    let dir = std::env::temp_dir().join(format!("vr37_{}_{}", 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 = std::process::Command::new("rustc")
        .args([
            "--edition",
            "2024",
            "--crate-type=lib",
            "--emit=metadata",
            "--out-dir",
        ])
        .arg(&dir)
        .arg(&file)
        .output()
        .expect("rustc 실행");
    String::from_utf8_lossy(&out.stderr).into_owned()
}

/// tokio::spawn과 같은 경계를 가진 가짜 spawn
const SPAWN_LIKE: &str = r#"
use std::future::Future;
pub fn spawn_like<F>(f: F) where F: Future + Send + 'static, F::Output: Send + 'static { drop(f); }
"#;

fn main() {
    // 1. 지역 변수를 빌리면 E0373: async 블록이 함수보다 오래 살 수 있다
    let e = compile_errors(
        &format!(
            "{SPAWN_LIKE}pub fn f() {{ let v = vec![1, 2]; spawn_like(async {{ println!(\"{{v:?}}\"); }}); }}\n"
        ),
        "borrow",
    );
    println!("--- 빌린 채로 spawn ---\n{}", e.lines().next().unwrap());
    assert!(e.contains("error[E0373]"), "{e}");
    assert!(
        e.contains("help: to force the async block to take ownership"),
        "{e}"
    );

    // 2. async move로 옮기면 통과
    let e = compile_errors(
        &format!(
            "{SPAWN_LIKE}pub fn f() {{ let v = vec![1, 2]; spawn_like(async move {{ println!(\"{{v:?}}\"); }}); }}\n"
        ),
        "move",
    );
    assert!(!e.contains("error"), "{e}");

    // 3. 'static은 "영원히 산다"가 아니다: String을 옮겨 넣으면 'static 경계를 채우고, 태스크가 끝나면 버려진다
    let e = compile_errors(
        &format!(
            "{SPAWN_LIKE}pub fn f() {{ let s = String::from(\"x\"); spawn_like(async move {{ s.len() }}); }}\n"
        ),
        "static",
    );
    assert!(!e.contains("error"), "{e}");

    // 4. 여러 태스크가 같은 값을 보려면 Arc로 나눠 준다
    let e = compile_errors(
        &format!(
            "{SPAWN_LIKE}use std::sync::Arc;\npub fn f() {{ let v = Arc::new(vec![1]); for _ in 0..2 {{ let v = v.clone(); spawn_like(async move {{ v.len() }}); }} }}\n"
        ),
        "arc",
    );
    assert!(!e.contains("error"), "{e}");

    println!("ALL CHECKS PASSED");
}