Guarantees About the Lifetime of a Reference in a Local Variable

Guarantees about the lifetime of a reference in a local variable

Swift makes no guarantee about the the lifetime of an object until
the end of the closest surrounding scope, see for example
the following threads in the Swift Forum:

  • Should Swift apply “statement scope” for ARC
  • ARC // Precise Lifetime Semantics

where it is stated you can use withExtendedLifetime(_:_:):

Evaluates a closure while ensuring that the given instance is not destroyed before the closure returns.

for that purpose. As for the rationale,
Dave Abrahams (Apple) states:

The lack of such a guarantee, which is very seldom actually useful
anyhow, is what allows us to turn costly copies (with associated
refcount traffic and, often CoW allocation and copying fallout) into
moves, which are practically free. Adopting it would basically kill our
performance story for CoW.

And Joe Groff (Apple) in the same thread:

Yeah, if you want to vend resources managed by an object to consumers outside of that object like this, you need to use withExtendedLifetime to keep the object alive for as long as you're using the resources. A cleaner way to model this might be to put the class or protocol in control of handling the I/O to the file handle, instead of vending the file handle itself, so that the ownership semantics fall out more naturally:

Update (Jan 2022): It is currently being discussed whether or not to introduce lexical lifetimes for objects. For the details, see A roadmap for improving Swift performance predictability: ARC improvements and ownership control in the Swift forum.

Lifetime of weak local variables with ARC

The conventions of autoreleasing and allocing still apply in the world of ARC. The only difference is that ARC will insert extra retain/release calls to make it much harder to leak objects or access a dealloced object.

In this code:

__weak NSString *str = [[NSString alloc] initWithFormat:@"%@", [NSDate date]];

The only place the object is retained (or equivalent) is the alloc. ARC will automatically insert a release command, causing it to be immediately dealloced.

Meanwhile, in this code:

 __weak NSString *str = [NSString stringWithFormat:@"%@", [NSDate date]];

By convention, the return value of a convenience constructor like this must be an autoreleased object*. That means the current autoreleasepool has retained the object and will not release it until the pool is drained. You are therefore all but guaranteed that this object will exist for at least the duration of your method - although you probably shouldn't rely on this behaviour.

(* or retained in some other way)

Rust cannot return value referencing local variable on HashMap get

fn get_hash_map() -> Option<&'static Vec<i32>> {
let mut hm = HashMap::new();
let mut vec = Vec::new();
vec.push(1);
hm.insert("1".to_string(), vec);
return hm.get("1");
}

This is invalid, because you have declared you are going to return an Option<&'static Vec<i32>>, but you are returning an Option<&'a Vec<i32>> where 'a is the current function lifetime. The HashMap will stop existing as soon as the function returns, freeing the vector, and the reference will then become dangling. This is the exact kind of situation the borrow checker is designed to avoid.

Just return the vector by value:

fn get_hash_map() -> Option<Vec<i32>> {
let mut hm = HashMap::new();
let mut vec = Vec::new();
vec.push(1);
hm.insert("1".to_string(), vec);
return hm.remove("1");
}

remove moves the value out of the map, returning it as an Option<V>.

If you then need an Option<&Vec<i32>>, you can just use as_ref() on your Option<Vec<i32>> to get one. Always remember it will become invalid as soon as its value goes out of scope.

Life time of local variable in Java

An object is only eligible for garbage collection after the last reference to it is no longer reachable from your code.

Let's count the references to your newly created object. Each class1 object is only referenced from the list at the moment, so let's look at the references to the list itself.

You have one reference called list1.

Then you return a value. This creates a second reference, which is placed in the stack and passed up to the caller.

List<...> result = theObj.methodName("foo");

During that process the first reference (list1) is no longer accessible, but the reference that was returned back is still accessible - it will be assigned to the variable result in this case. So you still have one valid and accessible reference to the list. Therefore, every reference inside the list is also valid and accessible.

So yes, it is completely safe to use.

There are languages other than Java, where you can allocate an object in stack space. That is, the object itself is allocated locally. Returning a reference to such an object is unsafe as the stack frame is popped. Take this example in C, that returns a pointer to the beginning of a local array:

char *bad_string(void)
{
/* BAD BAD BAD */
char buffer[] = "local string";
return buffer;
}

But in Java, objects are always allocated in heap space (barring internal optimizations which are not visible to the programmer and are checked to be safe, as mentioned in the comments).

An object allocated by the new operator is never "popped", and always obeys the rules of garbage collection. Stack/local method space is only used for primitives and references, and you never get a reference to any of those, only to objects. Thus, if you have a reference to something, you can trust that its memory is safe.

URLSessionTask instance variable vs local variable - who owns the reference?

Are you asking which pattern is correct? Neither. The URLSession owns the data task and manages its memory as soon as you resume it for the first time, so there is no need for you to keep any reference to it, unless you plan to do something else with that reference such as configuring the task further or cancelling the operation later. Generally it is sufficient and quite usual to say

urlSession.dataTask(with:url) { data, resp, err in
// whatever
}.resume()

Swapping two local references leads to lifetime error

Obviously, x and y have different lifetimes, but why should compiler require y to live as long as x?

Because of the signature of std::mem::swap:

pub fn swap<T>(x: &mut T, y: &mut T)

T is the type of the argument to foo, which is a reference of some lifetime chosen by the caller of foo. In the 2018 edition of Rust, the latest compiler gives a slightly more detailed error message in which it calls this lifetime '1. Calling std::mem::swap requires the type of x, &'1 T, to be the same as the type of y, but it can't shrink the lifetime of x to match that of y because the lifetime of x is chosen by the caller, not by foo itself. Vikram's answer goes into more detail on why the lifetime cannot be shrunk in this case.

I am essentially only modifying references locally inside foo and referenced objects are guaranteed to exist

This is true, but it doesn't give you any freedom with respect to the lifetime of x inside foo. To make foo compile, you have to give the compiler another degree of freedom by making a new borrow of which the compiler can choose the lifetime. This version will compile (playground):

pub fn foo<T: Copy>(x: &T) {
let mut x = &*x;
...
}

This is called reborrowing, and it happens implicitly in some cases, for example, to the receiver of a method call that takes &mut self. It does not happen implicitly in the case you presented because swap is not a method.

Guaranteed lifetime of temporary in C++?

The destructor for that sort of temporaries is called at the end of the full-expression. That's the most outer expression which is not part of any other expression. That is in your case after the function returns and the value is evaluated. So, it will work all nice.

It's in fact what makes expression templates work: They can keep hold references to that sort of temporaries in an expression like

e = a + b * c / d

Because every temporary will last until the expression

x = y

Is evaluated completely. It's quite concisely described in 12.2 Temporary objects in the Standard.



Related Topics



Leave a reply



Submit