By nodakai


2017-04-15 17:10:33 8 Comments

fn main() {
    let mut x: Vec<&i32> = vec![];
    let a = 1;
    x.push(&a);
    drop(x);
    // x.len(); // error[E0382]: use of moved value: `x`
}  // `a` dropped here while still borrowed

The compiler knows drop() drops x (as evident from the error in the commented-out code) but still thinks the variable is borrowing from a! This is unfair!

Should this be considered as one of numerous dupes of rust-lang/rust#6393 (which is now tracked by rust-lang/rfcs#811?) But the discussion there seems to be centered on making &mut self and &self coexist in a single block.

2 comments

@Peter Hall 2017-04-15 18:11:58

The compiler knows drop() drops x (as evident from the error in the commented-out code)

The Rust compiler doesn't know anything about drop and what it does. It's just a library function, which could do anything it likes with the value since it now owns it.

The definition of drop, as the documentation points out, is literally just:

fn drop<T>(_x: T) { }

It works because it the argument is moved into the function, and is therefore automatically dropped by the compiler when the function finishes.

If you create your own function, you will get exactly the same error message:

fn my_drop<T>(_x: T) { }

fn main() {
    let mut x: Vec<&i32> = vec![];
    let a = 1;
    x.push(&a);
    my_drop(x);
    x.len();
}

This is exactly what is meant in the documentation when it says drop "isn't magic".

@Lukas Kalbertodt 2017-04-15 18:09:36

I can't give you a definite answer, but I'll try to explain a few things here. Let's start with clarifying something:

The compiler knows drop() drops x

This is not true. While there are a few "magic" things in the standard library that the compiler knows about, drop() is not such a lang item. In fact, you could implement drop() yourself and it's actually the easiest thing to do:

fn drop<T>(_: T) {}

The function just takes something by value (thus, it's moved into drop()) and since nothing happens inside of drop(), this value is dropped at the end of the scope, like in any other function. So: the compiler doesn't know x is dropped, it just knows x is moved.


As you might have noticed, the compiler error stays the same regardless of whether or not we add the drop() call. Right now, the compiler will only look at the scope of a variable when it comes to references. From Niko Matsakis' intro to NLL:

The way that the compiler currently works, assigning a reference into a variable means that its lifetime must be as large as the entire scope of that variable.

And in a later blog post of his:

In particular, today, once a lifetime must extend beyond the boundaries of a single statement [...], it must extend all the way till the end of the enclosing block.

This is exactly what happens here, so yes, your problem has to do with all this "lexical borrowing" stuff. From the current compilers perspective, the lifetime of the expression &a needs to be at least as large as the scope of x. But this doesn't work, since the reference would outlive a, since the scope of x is larger than the scope of a as pointed out by the compiler:

= note: values in a scope are dropped in the opposite order they are created

And I guess you already know all that, but you can fix your example by swapping the lines let mut x ...; and let a ...;.


I'm not sure whether or not this exact problem would be solved by any of the currently proposed solutions. But I hope that we will see soon enough, as all of this is being addressed as part of the Rust 2017 roadmap. A good place to read up on the updates is here (which also contains links to the five relevant blog posts of Niko).

@Peter Hall 2017-04-15 18:23:24

You beat me to it by 2 minutes! I thought this was my chance to answer a Rust question while Shepmaster seemed to be taking a break :)

@Lukas Kalbertodt 2017-04-15 18:28:38

@PeterHall Oh no, I'm sorry :( You could join us in chat if you like ^_^

@nodakai 2017-04-15 21:36:22

"So: the compiler doesn't know x is dropped, it just knows x is moved." I'm intrigued, are you trying to say anything here?

@Lukas Kalbertodt 2017-04-15 21:44:38

@nodakai Yes, "moving" != "dropping". For example, a normal vector can be moved without being dropped. In fact by moving it can live longer. An extreme example of that is mem::forget(): the argument is moved into that function but is never ever dropped. Does this help or is anything still unclear?

@nodakai 2017-04-15 21:55:57

forget() (to be more precise drop_in_place()) is a compiler intrinsic which is exceptional to Rust's linear type system. I don't think it's fair to refer to it here. Otherwise any functions which take a parameter via move and doesn't return anything (for example) should be understood as data "sink" which drop the moved data.

@Lukas Kalbertodt 2017-04-15 22:07:52

@nodakai You're right, forget() is not an optimal example here. But functions taking types by value do not necessarily drop them, even if they don't return anything. They could store the value in a static (example) or send it to another thread. When you only move a value into a function, then yes, the value is dropped when the function exits. But the function can move the value somewhere else, e.g. another thread. And thta something else can live longer than the function.

@nodakai 2017-04-15 23:27:51

Again, what are you trying to claim by throwing a bunch of unsafe code to me? Anything can happen with unsafe (granted, it won't immediately turn off all the type checks but it does open up a way to circumvent them, like your example did under the hood.) But +1 to your mention to multithreading, I didn't specifically exclude Send types which Vec<&'a i32> in my original question happened not to implement.

@Lukas Kalbertodt 2017-04-16 06:07:43

@nodakai I'm not quite sure what you're trying to achieve with this discussion :/ As far as I can see, your question is answered. Your comments look like you want to argue against me instead of trying to understand. My remark that drop() isn't special is certainly true, because even the docs clearly state that. Whether or not a function always has to drop or return an owned value when not using unsafe code, that I think is an interesting question! But we shouldn't explore it here in the comments, but by asking a new question here on SO. And btw: Vec<&i32> does implement Send.

Related Questions

Sponsored Content

1 Answered Questions

[SOLVED] Rust error: borrow occurs after drop a mutable borrow

2 Answered Questions

1 Answered Questions

[SOLVED] Factory method: instance does not live long enough

  • 2015-05-02 20:24:30
  • W.K.S
  • 572 View
  • 2 Score
  • 1 Answer
  • Tags:   rust

0 Answered Questions

Confused by "temporary value dropped here while still borrowed"

  • 2018-06-12 08:31:51
  • SK.Chen
  • 222 View
  • 1 Score
  • 0 Answer
  • Tags:   rust lifetime

0 Answered Questions

1 Answered Questions

[SOLVED] Cannot move out of borrowed content

  • 2015-01-26 21:04:41
  • Peekmo
  • 51612 View
  • 102 Score
  • 1 Answer
  • Tags:   rust

1 Answered Questions

0 Answered Questions

Nested method calls with &mut receivers result in borrow checker errors

  • 2016-12-27 22:00:30
  • mnm364
  • 125 View
  • 1 Score
  • 0 Answer
  • Tags:   rust

0 Answered Questions

Multiple self borrows

  • 2016-10-31 22:09:22
  • GregoryComer
  • 136 View
  • 0 Score
  • 0 Answer
  • Tags:   rust borrowing

1 Answered Questions

[SOLVED] "cannot move out of borrowed content" with operator overloading

  • 2016-05-19 03:13:09
  • Paul A Jungwirth
  • 630 View
  • 8 Score
  • 1 Answer
  • Tags:   rust

Sponsored Content