Why async_std::println! macro is not thread-safe?

rustc version:

❯ rustc --version
rustc 1.64.0-nightly (1c7b36d4d 2022-07-12)

my code:

#[tokio::main]
async fn main() {
    tokio::spawn(async {
        foo().await;
    }).await;
}

async fn foo() {
    async_std::println!("hi").await;
}

cargo check with -Z macro-backtrace:

❯ cargo check
    Checking asdf v0.1.0 (/home/jo/asdf)
error[E0277]: `core::fmt::Opaque` cannot be shared between threads safely
   --> src/main.rs:3:5
    |
3   |     tokio::spawn(async {
    |     ^^^^^^^^^^^^ `core::fmt::Opaque` cannot be shared between threads safely
    |
    = help: the trait `Sync` is not implemented for `core::fmt::Opaque`
    = note: required because of the requirements on the impl of `std::marker::Send` for `&core::fmt::Opaque`
    = note: required because it appears within the type `ArgumentV1<'_>`
    = note: required because it appears within the type `[ArgumentV1<'_>; 0]`
    = note: required because it captures the following types: `ResumeTy`, `&str`, `[&str; 1]`, `&[&str]`, `&[&str; 1]`, `[ArgumentV1<'_>; 0]`, `&[ArgumentV1<'_>]`, `&[ArgumentV1<'_>; 0]`, `Arguments<'_>`, `impl for<'r> Future<Output = ()>`, `()`
note: required because it's used within this `async` block
   --> /home/jo/.cargo/registry/src/github.com-1ecc6299db9ec823/async-std-1.12.0/src/macros.rs:89:29
    |
87  |   macro_rules! println {
    |   -------------------- in this expansion of `async_std::println!`
88  |       () => ($crate::print!("\n"));
89  |       ($($arg:tt)*) => (async {
    |  _____________________________^
90  | |         $crate::io::_print(format_args!($($arg)*)).await;
91  | |         $crate::io::_print(format_args!("\n")).await;
92  | |     })
    | |_____^
    |
   ::: src/main.rs:9:5
    |
9   |       async_std::println!("hi").await;
    |       ------------------------- in this macro invocation
    = note: required because it captures the following types: `ResumeTy`, `[static generator@/home/jo/.cargo/registry/src/github.com-1ecc6299db9ec823/async-std-1.12.0/src/macros.rs:89:29: 92:6]`, `impl Future<Output = ()>`, `()`
note: required because it's used within this `async fn` body
   --> src/main.rs:8:16
    |
8   |   async fn foo() {
    |  ________________^
9   | |     async_std::println!("hi").await;
10  | | }
    | |_^
    = note: required because it captures the following types: `ResumeTy`, `impl Future<Output = ()>`, `()`
note: required because it's used within this `async` block
   --> src/main.rs:3:24
    |
3   |       tokio::spawn(async {
    |  ________________________^
4   | |         foo().await;
5   | |     }).await;
    | |_____^
note: required by a bound in `tokio::spawn`
   --> /home/jo/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.20.0/src/task/spawn.rs:127:21
    |
127 |         T: Future + Send + 'static,
    |                     ^^^^ required by this bound in `tokio::spawn`

For more information about this error, try `rustc --explain E0277`.
error: could not compile `grep-asynchronous` due to previous error

I really don't get the cargo check message. Should I just use the std println macro instead?

The format_args! macro internally puts all values to be printed into opaque containers. Those opaque containers never implement Send or Sync as the contained value may implement neither. I wouldn't be surprised if this problem you hit related to the reason that async_std::println!() is marked as unstable.

2 Likes