Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

avoid copies when returning by value #828

Open
zygoloid opened this issue Sep 14, 2021 · 1 comment
Open

avoid copies when returning by value #828

zygoloid opened this issue Sep 14, 2021 · 1 comment
Labels
leads question A question for the leads team

Comments

@zygoloid
Copy link
Contributor

Instead of guaranteeing that a copy happens when returning by value, could we give a weaker guarantee that there is no lifetime issue, but that we may or may not make a copy?

What I'm thinking is: we could say that a function that returns by value may return a reference to anything that it knows lives at least as long as any of its parameters (including me). Then at the call site, we can check whether the returned value outlives any of the function parameters and make a copy if so. Then:

// F doesn't make a copy; returned value lives at least as long as parameter s
fn F(s: String) -> String { return s; }
// G makes a copy: v doesn't live long enough
fn G(s: String) -> String {
  let v: String = "foo"   s   "bar";
  return v;
}
fn Print(s: String);
fn H() {
  // Parameter of `F` can be kept alive until `;`, so we know
  // the return value lives at least that long and don't need 
  // to make any copies.
  Print(F("hello"));
  // `x` outlives the function argument, so we make a copy here.
  let x: String = F("hello");
  Print(x);
}

I suppose we can avoid making a copy even in the second case in H by lifetime-extending the "hello" temporary. I'm not sure that's a good idea; it might be too unpredictable.

One problem with this is that the function return is creating an immutable view, and we need the callee to know how long it's promising that returned value will remain immutable for. I suppose this is nothing new; this is analogous to a classic C issue:

const string &s = v[i];
v.push_back("x");
use(s);

... where this either works or fails depending on whether v[i] produces a reference to an existing object (eg, v is vector<string>) or ends up binding s to a temporary (eg, v is vector<const char*>). We might want some simple syntax to force a copy and end a chain of immutable views. (You could use var for that, but that also implies mutability, which might be undesirable.)

There's also a calling convention complexity issue with this kind of approach: if F can either return a handle to some existing object or copy to some caller-provided storage, then the caller always needs to provide the storage and may need to perform a branch to tell whether it should provide a copy. That seems like something we could handle but I'm not sure whether it'll be worthwhile unless we get to avoid a lot of copies.

Originally posted by @zygoloid in #821 (comment)

@github-actions

This comment was marked as outdated.

@github-actions github-actions bot added the inactive Issues and PRs which have been inactive for at least 90 days. label Dec 14, 2021
@zygoloid zygoloid added leads question A question for the leads team and removed inactive Issues and PRs which have been inactive for at least 90 days. labels Dec 14, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
leads question A question for the leads team
Projects
None yet
Development

No branches or pull requests

1 participant