Why does future print if its never called?

use futures::executor::block_on;

async fn hello_future() {
    println!("hello, future!");
}

 fn hello_world() {
    println!("hello, world!");
}

fn main() {
    let future1 = hello_future(); // Nothing is printed

    let future = hello_world(); 
    block_on(future1); // `future` is run and "hello, world!" is printed
}

(Playground)

Output:

hello, world!
hello, future!

Errors:

   Compiling playground v0.0.1 (/playground)
warning: unused variable: `future`
  --> src/main.rs:14:9
   |
14 |     let future = hello_world(); 
   |         ^^^^^^ help: if this is intentional, prefix it with an underscore: `_future`
   |
   = note: `#[warn(unused_variables)]` on by default

warning: `playground` (bin "playground") generated 1 warning
    Finished dev [unoptimized + debuginfo] target(s) in 1.36s
     Running `target/debug/playground`

What do you mean by "never called"? It is executed by block_on, as you've noted yourself in the comment.

2 Likes

block_on executes future1 from line 12,

the compiler itself says future from line 14 is unused. yet we see its output

the comments are from

https://rust-lang.github.io/async-book/01_getting_started/04_async_await_primer.html

I should had removed them

Well, hello_world is sync, so it doesn't return a Future - it executes right at the time of calling, and future binding has type ().

3 Likes

ok so this still prints to the output

fn hello_world() {
   println!("hello, world!");
}

fn main() {

   let future = hello_world(); 
   
}

im just confused about it being a statement or expression, seems to me like it shouldn't print anything

Well, first of all, every statement is an expression (which evaluates to ()).

Now, hello_world() is an expression, which is executed by calling the function; since the function is ordinary, this means executing its body immediately, including println!. Then the value of this expression (that is, the return value of function; that is, (), since you don't return anything explicitly) is assigned to future.

When you execute hello_future, however, the logic is different - evaluating an async function call expression means building the future which will execute the function body when polled. Therefore, this future is assigned to future1, and the println is executed when this future is driven by block_on.

3 Likes

Not every function in Rust is async. If you call a regular, non-async function, it will do its thing right there and then.

1 Like

Why? You are calling function. It's executed and returns a value.

Is this confusing:

fn hello_world() {
   println!("hello, world!");
}

fn main() {
   hello_world();   
}

Or this:

fn hello_world() {
   println!("hello, world!");
}

fn main() {
   let ignore_me = hello_world(); 
}

Or this:

fn hello_world() {
   println!("hello, world!");
}

fn main() {
   let future = hello_world(); 
}

It think you have confused yourself by naming variable which is not Future but just an empty type (called () in Rust) future.

But I can assure you: compiler treats all variable names equally! Except for _ (single underscore) which is not, technically, a variable name (it's a placeholder).

4 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.