2

I'am trying to implement educational client-server application using Rust and Iron. I've encountered the behaviour that I can't understand. Here is the code:

fn main() {
    Iron::new(hello_world).http("localhost:3000").unwrap();

    let mut input = String::new();
    io::stdin().read_line(&mut input)
        .expect("Failed to read line");

    println!("You entered: {}", &input)
}


fn hello_world(_: &mut Request) -> IronResult<Response> {
    Ok(Response::with((status::Ok, "Hello World!")))
}

When I run it and try to enter something from the keyboard, the line You entered: Some text is not appearing.

But after I changed this line:

Iron::new(hello_world).http("localhost:3000").unwrap();

With this:

let listener = Iron::new(hello_world).http("localhost:3000").unwrap();

I got string You entered: Some text on my console. So it seems to work. But now I have warning about unused variable. This behaviour is confusing.

Can anyone explain why this actually happens?

  • 1
    Because when you drop the listener it will block till the server dies, when you move it to a variable, it'll block when the variable is dropped (i.e. scope end) -- you can prefix the variable with `_` (like `_listener`) to silence unused warnings. – набиячлэвэли Apr 09 '17 at 20:11
  • Thanks you. And thanks for nice hack with `_listener`. I didn't know about it. – Vladislav Sazanovich Apr 10 '17 at 04:27

1 Answers1

2

In the first version of your code, the first line will block waiting for incoming connections. This is because of the following:

  1. Iron::new(hello_world).http("localhost:3000").unwrap() produces an object of type Listening, which will start listening to http requests in a separate thread.
  2. The Listening struct implements the Drop trait, i.e. any objects of type Listening will run a drop function when they fall out of scope. Said drop function will join the listening thread, blocking further execution of your program.
  3. By not assigning the Listening object to a variable, it falls out of scope immediately. This means that the drop function is run right after the object's creation.

Alternative explanation in code

The first version of your program:

fn main() {
    Iron::new(hello_world).http("localhost:3000").unwrap();
    // The listening thread is joined here, so the program blocks
    // The instructions below will never be executed

    let mut input = String::new();
    io::stdin().read_line(&mut input)
        .expect("Failed to read line");

    println!("You entered: {}", &input)
}

The results of introducing a variable:

fn main() {
    let listener = Iron::new(hello_world).http("localhost:3000").unwrap();

    let mut input = String::new();
    io::stdin().read_line(&mut input)
        .expect("Failed to read line");

    println!("You entered: {}", &input)

    // The listening thread is joined here, so the program blocks
    // As you can see, the program will not exit
}
aochagavia
  • 5,887
  • 5
  • 34
  • 53
  • Thanks for the thorough explanation! Do this pattern have a name in Rust? – Vladislav Sazanovich Apr 10 '17 at 04:24
  • I mean, is it a common practice to do so? – Vladislav Sazanovich Apr 10 '17 at 04:30
  • This is a common pattern, not only in Rust. C++ has destructors, for instance, which are also executed when an object falls out of scope. You can read more about `Drop` in the [Rust Book](https://doc.rust-lang.org/nightly/book/first-edition/drop.html) – aochagavia Apr 10 '17 at 06:12
  • The pattern is very useful in languages without garbage collection, like Rust. You can use it to ensure any memory allocated by your object is freed when the object is destroyed (otherwise you would have to do that manually, with a chance of forgetting to do it and introducing a memory leak). This pattern is called [RAII](https://en.wikipedia.org/wiki/Resource_acquisition_is_initialization) in C++, and I have seen the same name used in Rust context (see, for instance, [this resource](http://rustbyexample.com/scope/raii.html)) – aochagavia Apr 10 '17 at 06:17