You can return stack values assuming you're willing to transfer ownership to the calling function. For example, this is totally valid (and does not heap allocate):
struct Point {
x: i32,
y: i32,
}
fn add(p1: &Point, p2: &Point) -> Point {
Point {
x: p1.x + p2.x,
y: p1.y + p2.y,
}
}
You'll also find that a lot of common data structures already use the heap under the hood (e.g. Vec and HashMap), so there's no need to Box them.
I'm not quite sure what you mean by "the lifetime of a function"; lifetimes are associated with references, but not with functions themselves. In particular, it can be totally valid to return a reference to something that needs to last beyond the current function call; lifetimes are used to enforce that you'll get a compiler error if the memory your reference points to won't survive long enough. For example, it's totally valid to have a function return a reference to a field on a struct as long as the struct was also passed in by reference:
fn x_val(p: &Point) -> &i32 {
&p.x
}
Since `p` is passed in by reference, the compiler knows that the reference to `x` will still be valid after the function returns, so it allows that reference to be returned. The compiler will even catch it if later you drop the Point while the reference to the `x` value is still alive:
fn main() {
let p = Point { x: 1, y: -1 };
let x: &i32 = x_val(&p);
std::mem::drop(p);
// Without this line, this will compile, since the compiler can tell that `x` isn't
// used after `p` is dropped. However, since `x` is used here afterwards, the
// compiler will generate an error stating that `p` does not live long enough.
println!("{}", x);
}