Can not understand: "temporary value dropped while borrowed"

#1

Using futures-preview 0.3.0-alpha.10 and rustc 1.33.0-nightly (96d1334e5 2018-12-14).
Trying to connect in parallel to multiple servers and get the one which response first.

    pub async fn connect<'a>(bootstrap: &'a Vec<&'a str>) -> io::Result<Self> {
        let connectFutures = bootstrap.iter().
            filter_map(|addr| addr.parse().ok()).
            map(|addr|Broker::connect(addr));

        // TODO: use `fold` to preserve and report last error if no result
        let resolved = futures::stream::futures_unordered(connectFutures).
                filter_map(|f| future::ready(f.ok())).
                next();
        let broker = match await!(resolved) {
            Some(broker) => broker,
            // TODO: failure
            None => return Err(io::Error::from(io::ErrorKind::NotFound)),
        };
...

Getting error:

error[E0716]: temporary value dropped while borrowed
  --> src/cluster.rs:45:24
   |
45 |           let resolved = futures::stream::futures_unordered(connectFutures).
   |  ________________________^
46 | |                 filter_map(|f| future::ready(f.ok())).
   | |_____________________________________________________^ creates a temporary which is freed while still in use
47 |                   next();
   |                         - temporary value is freed at the end of this statement
48 |           let broker = match await!(resolved) {
   |                                     -------- borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value

Breaking it down into separate statements does compile:

        let resolved1 = futures::stream::futures_unordered(connectFutures);
        let mut resolved2 = resolved1.filter_map(|f| future::ready(f.ok()));
        let resolved3 = resolved2.next();
        let broker = await!(resolved3);
        let broker = match broker {
            Some(broker) => broker,
            // TODO: failure
            None => return Err(io::Error::from(io::ErrorKind::NotFound)),
        };

I do understand that it is related to the fact that lifetime of “chained” statements is limited to the statement itself, but compiler totally failed to convey which value it considers temporary. It looks like it is complaining about “resolved” but why?

0 Likes

#2

Try putting it all in one line.

0 Likes

#3

Same error but more complicated:

        let broker = match await!(
          futures::stream::futures_unordered(connectFutures).
              filter_map(|f| future::ready(f.ok())).
              next()
        ) {
            Some(broker) => broker,
            // TODO: failure
            None => return Err(io::Error::from(io::ErrorKind::NotFound)),
        };
error[E0716]: temporary value dropped while borrowed
  --> src/cluster.rs:65:11
   |
64 |             let broker = match await!(
   |    ____________________________-
   |   |____________________________|
   |  ||
65 |  ||           futures::stream::futures_unordered(connectFutures).
   |  ||___________^
66 | |||               filter_map(|f| future::ready(f.ok())).
   | |||___________________________________________________^ creates a temporary which is freed while still in use
67 |  ||               next()
68 |  ||         ) {
   |  ||         -
   |  ||_________|
   |  |__________temporary value is freed at the end of this statement
   |             borrow later used here
   |
   = note: consider using a `let` binding to create a longer lived value
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
0 Likes

#4

One line, not one statement.

Sorry, I should’ve been clearer:

let resolved = futures::stream::futures_unordered(connectFutures).filter_map(|f| future::ready(f.ok())).next();
0 Likes

#5

I see, in one line the span of error becomes more clear. But I still do not understand which value is temporary. I start thinking that it is because I create future::ready inside in closure, it is being destroyed as soon as expression ends.
And when I split it into individual let bindings, expression now gets lifetime of the binding and lives long enough.

0 Likes

#6

I don’t know anything about futures, but could you link me to the method next()'s rustdoc?

0 Likes

#7

When you ask for help here, it is very helpful if you post a minimal viable example, and use playground. This will greatly improve your changes of getting help, because people can play with your code and see exactly where it is going wrong.

As you show the problem now it is very hard to give any meaningful help.


edit:

after reading through the docs for futures I think I know what your problem is. To explain it i will use a simpler example.

let foo = "FooBar".to_string().as_mut_str();
println!("{}", foo)

This has the same error as your code. Here’s why,
This code is equivalent to writing,

let foo = {
    let mut __temp = "FooBar".to_string();
    __temp.as_mut_str()
};

println!("{}", foo);

Note that the signature of String::as_mut_str is as_mut_str(&mut self) -> &mut str. Note that the lifetime of self if tied to the lifetime of the return type. (if you don’t see it, note that the desugaring of this turns the signature into as_mut_str<'a>(&'a mut self) -> &'a mut str).
Now the problem is obvious. You are trying to create a reference to a temporary that has been dropped.
The solution is also obvious, just split creating the value from taking a reference to it.

let mut foo = "FooBar".to_string();
let foo = foo.as_mut_str();
println!("{}", foo);

Now back to your example, the relevant line of code is

let resolved = futures::stream::futures_unordered(connectFutures).
                filter_map(|f| future::ready(f.ok())).
                next();

the signature for next is fn next(&mut self) -> Next<'_, Self>, oh look a lifetime that is tied to self. Now the solution is obvious. Simply split creating the value from taking a reference to it.

let mut resolved = futures::stream::futures_unordered(connectFutures).
                filter_map(|f| future::ready(f.ok()));
let resolved = resolved.next();
3 Likes