use std::sync::Arc;
use std::ops::Deref;
fn main() {
let result = &*Arc::new(5);
// if we replace the line with the below it does not compile!
//let result = Arc::new(5).deref();
println!("{result:?}")
}
So what I am doing is creating a new Arc and immediately calling deref on it. This is of course a silly example but in more complicated scenarios imagine instead of Arc::new(5) a function that does some stuff and at the end returns an Arc (that might not be new but maybe even cloned).
In the example above I do &*Arc::new(5) which is equivalent to &*(Arc::new(5).deref()). This compiles fine, but if I would drop the &* and only do Arc::new(5).deref() it does not compile anymore with this error message:
error[E0716]: temporary value dropped while borrowed
So my question is what does the &* in this situation help here? Does it somehow handle the temporary value? We still get an Arc that we not store anywhere in a variable and just access the reference inside, how can the compiler know that this reference lives long enough? I assume it most hold the Arc for longer than just the single line where Arc::new is called, so how does this work under the hood?
I tried to search the reference for answers on this but couldn't really find a suiting explanation of this.
I believe the & is necessary for temporary lifetime extension. Without it, like Arc:new(5).deref() in your example, no temporary lifetime extension is performed. See also:
The borrowck phase never affects how a program behaves. Either the program as written passes borrowck or not.
Therefore Rust never extends the lifetime of temporaries just to satisfy borrowck. It has to decide that without reference to lifetime constraints, and I think that's why lifetime extension is triggered by syntax only.
let oiskpa = Bnk::cni(5).owefn(); // doesn't look like it needs temporary lifetime extended
let result = Arc::new(5).deref(); // same syntax, so no lifetime extension here either
Thanks for your answer and that is an excellent link! I think I understood now what is happening. Although in the simple examples it is a bit easier than in a more complicated example where moving the owned value is not allowed / does not happen but its lifetime can still be extended, for example:
fn print_string(a: &Arc<String>) {
// let s = *a; // not allowed since `String` is not `Copy`
let s = &*a; // allowed and extends lifetime of `String`
println!("{s}");
}
So somehow here we cannot move out, but we can extend its lifetime (with the limit of the lifetime of the input &Arc I assume).
You can leverage this to prevent access to the owned type after creation. Do note that temporary lifetime extensions don't apply to _ without a let though:
struct Foo;
impl Drop for Foo {
fn drop(&mut self) {
println!("dropped Foo");
}
}
struct Bar;
impl Drop for Bar {
fn drop(&mut self) {
println!("dropped Bar");
}
}
struct Fizz;
impl Drop for Fizz {
fn drop(&mut self) {
println!("dropped Fizz");
}
}
fn main() {
// `Foo` gets dropped immediately.
let _ = Foo;
// `Bar` gets dropped last.
let _ = &Bar;
// `Fizz` gets dropped immediately.
_ = &Fizz;
}
Note that, unfortunately, the term "lifetime" is overloaded in Rust. Temporary lifetime extension is about extending the drop scope of values -- where they may drop, if they haven't been moved beforehand.[1] In this function, you only have an &Arc<String>. The Arc<String> can't drop within this function, and the String won't drop until the last containing Arc<String> drops. So there's no changes to any drop scopes here -- no extending of any lifetimes in that sense of the word.
The other kind of lifetimes in Rust are those '_ things. For instance, the input to your function is really a &'a Arc<String> for some lifetime 'a. These lifetimes do not denote value liveness scopes, they denote the duration of borrows. Within the function, all you know about 'a is that it lasts at least as long as the function call; you cannot assume it lasts any longer than that. There's no way to extend the duration of the borrow you received. So there's no extending of any lifetimes in this sense of the word, either.
The main intersection between the two concepts is that borrowed things cannot drop (or be moved, or have a fresh &mut _ taken to them). As others have noted, these borrow-related errors are due to pass-or-fail checks which do not change the semantics of a program. Borrow checking does not change where things drop. Temporary lifetime extension is syntactical, and is not based on borrow checking '_ lifetimes.