Noob Question: Extending local variable lifetime

Hi all!

Thank you for your time; I promise I’ve done my best to find the answer to this question on my own.
I’m trying to write a super simple function to return an iterator over a buffer.

fn read_file<'a>(filename: String) -> Chars<'a> {
    let file = File::open(filename).expect("failed to open file");
    let mut reader = BufReader::new(file);
    let mut buf = String::new();
    reader.read_to_string(&mut buf);
    buf.chars()
}

I realize that the problem is that my string buf is stack allocated and needs a lifetime of 'a. However, I have no idea how to make this happen. I’ve tried things like boxing my iterator, boxing my string, and calling clone. However, I’m obviously missing something fundamental. I’ve read RustByExample and the Rust book, and I’m reading Programming Rust (Blandy and Orendorff).

Thank you for your time and attention. I appreciate the help!

You don’t. You don’t tell the compiler what the lifetime of something is; it’s lifetime is what the compiler says it is. You don’t “extend” local variable lifetimes, period.

First thing you can do is: don’t return Chars, return the String.

Second thing: take buf as a parameter. That way, the lifetime of buf is based on what was passed into it, and can thus live longer than the function does.

As an aside: pretty much any time you have a non-'static lifetime which only appears in the return type of a function, that isn’t going to work.

3 Likes

Thank you! Quick follow up:

Why does returning the String work but returning the Chars not work?

Because the String is owned, the Chars is borrowed. A borrow cannot outlive the thing it borrows.

Ok, I think that makes sense to me. I appreciate your response.

I'm currently went here in my searches about this in Rust.

Now, I know this is not possible (this is a great answer at StackOverflow on this https://stackoverflow.com/a/28109180/7884305), but then I ask, why couldn't this be done?

I propose extending the meaning of lifetime to locals, there it doesn't mean "constraint the options to anything compatible with this" like generic constraints (which lifetimes, I would say, are a kind of), but instead "make sure this variable lives at least as this lifetime".

I can't find the process of suggesting a new feature to the Rust language anywhere, so I'm posting this here.

This is extremely useful, and incorporates smoothly with the expectations (for me, at least, but I'm sure I'm not alone) from the compiler.

Rust does not have garbage collection. Locals are allocated on the stack within the stack frame of the called function, and thus disappear when that stack frame is automatically deallocated upon called function return. (In the literature this is often referred to as "popping the stack".)

Box adds a slight wrinkle to this, as it (sort of) allocates a local variable on the heap. I have occasionally wanted to return both a Box and a reference into the box from some function— that should be theoretically sound but it’s not expressible in Rust’s current type system. Instead, you have to rely on runtime checks with something like Rc.

1 Like

Box allocates an object on the heap, but the fat-pointer descriptor of the object ends up allocated on the stack. Unless that Box is being returned from the function, and thus allocated within the caller's stack frame, the fat pointer disappears when the stack is unwound. (Note that the heap-allocated object is dropped at end-of-scope, before stack unwinding.)

1 Like

See also: owning_ref. :slight_smile:

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.