Rust: again it's a bug

Created on 16 May 2019  路  7Comments  路  Source: rust-lang/rust

static gs : &str = "global str";

fn main() {
    let mut s3 = String::from("Hello, world!");
    let s4 = try_it(&s3[..]);
    println!("{}", s4);
    s3.clear();
    println!("s4={}", s4);
}

fn try_it(s: &str) -> &str {
    println!("{}", s);
    return gs;
}

compile error

error[E0502]: cannot borrow `s3` as mutable because it is also borrowed as immutable
40 |     let s4 = try_it(&s3[..]);
   |                      -- immutable borrow occurs here
41 |     println!("{}", s4);
42 |     s3.clear();
   |     ^^^^^^^^^^ mutable borrow occurs here
43 |     println!("s4={}", s4);
   |                       -- immutable borrow later used here *completely wrong, s4 has nothing to do with s3*

@cramertj 's reply

No, This is not a bug. The elided lifetimes in try_it are assuming that the output &str has the same lifetime as the input &str. To fix this, you can change try_it to -> &'static str.

two points

  1. compiler actually knows whether the returned slice is s3 or not
  2. adding &'static is ugly and cannot solve the problem, such as if(xxx) gs else s4

Most helpful comment

This is indeed behaving as expected.

compiler actually knows whether the returned slice is s3 or not

The compiler doesn't know, as it requires extra global analysis that it doesn't perform at this point in time. It conceivably could, but it would make compile times much longer as it would have to do full escape analysis.

adding &'static is ugly and cannot solve the problem, such as if(xxx) gs else s4

It would be interesting to see the actual problem you want to tackle. It seems to me that you'd want to do follow the easier/safer path and allocate a new String.

Understanding what your intention is might shape what the error messages look like in the future to actually communicate possible solutions in a cleaner way.

If Rust is serious, I meant not just a toy, then, it should treat such kind of feature seriously,

I would like to point out that most of the people working on the compiler are volunteers working on their free time, so I would ask that you please keep your criticism constructive.

try to leverage as much compiler's knowledge as possible

We try to do so, but there are practical limitations. The lifetime annotations ('a/'static) are there to communicate intent to the compiler and enable this type of analysis.

All 7 comments

If Rust is serious, I meant not just a toy, then, it should treat such kind of feature seriously, try to leverage as much compiler's knowledge as possible

This is not a bug,

compiler actually knows whether the returned slice is s3 or not

When you write

fn try_it(s: &str) -> &str { ... }

You are telling the compiler that the output come from the input regardless of the function body, it desugars to

fn try_it<'a>(s: &'a str) -> &'a str { ... }

But if you annotate it with 'static, then you are telling the compiler that there is no relation between the inputs and outputs. So it works.

fn try_it(s: &str) -> &'static str { ... }

adding &'static is ugly and cannot solve the problem, such as if(xxx) gs else s4

The compiler can't know which branch will be taken, so it will be as conservative as possible (even if the if condition is a compile time constant)

My take is rust add some static analysis to c++, but having more thing to be very confusing at the very first sight, I prefer to have C++ plus static analysis comparing to use Rust

This is indeed behaving as expected.

compiler actually knows whether the returned slice is s3 or not

The compiler doesn't know, as it requires extra global analysis that it doesn't perform at this point in time. It conceivably could, but it would make compile times much longer as it would have to do full escape analysis.

adding &'static is ugly and cannot solve the problem, such as if(xxx) gs else s4

It would be interesting to see the actual problem you want to tackle. It seems to me that you'd want to do follow the easier/safer path and allocate a new String.

Understanding what your intention is might shape what the error messages look like in the future to actually communicate possible solutions in a cleaner way.

If Rust is serious, I meant not just a toy, then, it should treat such kind of feature seriously,

I would like to point out that most of the people working on the compiler are volunteers working on their free time, so I would ask that you please keep your criticism constructive.

try to leverage as much compiler's knowledge as possible

We try to do so, but there are practical limitations. The lifetime annotations ('a/'static) are there to communicate intent to the compiler and enable this type of analysis.

@estebank @KrishnaSannasi Thanks for your very detailed reply, I am now understand the problem a bit more, my original question was closed to quickly, before anyone can get to my point. I know Rust is trying to resolve lots of issues such as following up ownerships, in C++, if you use a unique_ptr after it's moved, compiler will not give you anything, and you will get a runtime error, but Rust tries to get rid of that issue. However, the problem is not solved elegant, the try_it function is just a challenge to the compiler, no static annotated one implies "You are telling the compiler that the output come from the input regardless of the function body", which is a huge surprise to me, as there is no document or book explicitly mentions that. My sense is Rust maybe a language that tries to solve the problem but at some point, after solves 90% of the problem, it has to use some hacks to solve the rest, in my case, if try_it could return s or gs based on conditions, then I don't know how Rust is going to work, yes, other ways may workaround, but my sense is Rust does not have a very good theoretical proven that it's completed to all the problems it tries to solve, and that is the reason that I criticize it.
Again, thanks!

In the future post these sorts of questions on https://users.rust-lang.org/ you'll get more help there

Also, look up lifetime elison, that is what is happening here to determine the lifetimes

@andy-yx-chen

no static annotated one implies "You are telling the compiler that the output come from the input regardless of the function body", which is a huge surprise to me, as there is no document or book explicitly mentions that

As of today, this is indeed thoroughly explicited in the Rust Book, under the keywords "lifetime elision rules". I think it's worth checking it, because they made a pretty good work explaining this ;)

Was this page helpful?
0 / 5 - 0 ratings