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
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 ;)
Most helpful comment
This is indeed behaving as expected.
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.
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.
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.
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.